1. 구조체 사용의 장점
 간단한 성적 처리 프로그램을 작성한다고 가정했을 때, 학생의 이름, 국어, 영어, 수학, 평균 점수를 저장할 변수가 필요하다.
단순하게 변수를 선언하면 다음과 같다.

 char name[10];
 int korean, english, math;
 double average;
하지만 위의 변수들은 모두 1명의 학생을 위한 것이다. 만약 학생이 2명이라면 또 다른 변수를 선언해야하고 학생이
100명이라면 각 변수를 100개씩이나 선언해야 한다. 이런 불상사를 막을 수 있는 방법이 구조체이다.
구조체는 각 변수들을 하나로 묶어서 관리할 수 있도록 한다.

2. 구조체의 정의
 struct 태그명{
 데이터형 멤버명;
 ...
};

ex)
 struct grade{
 char name[10];
 int korean, english, math;
 double average;
}; //구조체의 정의도 C 문장이므로 마지막에 세미콜론(;)을 빼먹지 말자.

※ Tip
기본 데이터형(primitive data type) : char, short, int, float, double 등
파생 데이터형(derived data type) : 기본형으로부터 만들어낸 배열, 포인터, 구조체, 공용체, 열거체
사용자 정의형(user-defined data type) : 구조체처럼 프로그래머에 의해서 새로 만들어진 데이터형

※ 구조체의 크기
  struct grade{
 char name;
 short no;
 int korean;
 double average;
};
 grade 구조체의 크기는 단순히 멤버들의 크기의 합, char(1byte)+short(2byte)+int(4byte)+double(8byte) = 15byte일까?
무조건 그렇지는 않다. 구조체의 크기는 모든 멤버들의 합보다 크거나 같다. 그 이유는 메모리 정렬(alignment) 때문인데
대부분의 CPU는 메모리 접근을 할 때 1바이트, 2바이트, 4바이트 단위 등으로 접근하기 때문에 단위를 맞춰서 변수를
할당하면 효율적 접근이 가능해진다. 이때 메모리가 정렬되었다고 하는데 C 컴파일러는 효율적 메모리 접근을 위해 실제로
사용되지 않는 데이터 바이트를 삽입하기도 한다. 이것을 패딩(padding)이라고 한다. 이런 패딩으로 인해서 구조체의
크기가 더 커질 수 있는 것이다. 따라서 구조체의 정확한 크기를 구하려면 단순히 멤버들 크기의 합을 구하면 안되고
sizeof 연산자를 사용해야 한다.

3. 구조체 변수의 선언
 struct 태그명 변수명1, 변수명2, ...;
 ex) struct grade student1;
      struct grade student1, student2;
 구조체는 새로운 데이터형을 만드는 것이므로 구조체를 정의하는 것만으로 구조체 변수가 메모리에 할당되지 않는다.
구조체의 멤버들은 구조체 변수가 선언될 때에 비로소 메모리에 할당된다.

struct grade student1;
           name           korean           english               math           average 
           char[20]                      int                             int                                int                        double

                                                                           student1

 위와 같이 메모리에 할당되며 보는 바와 같이 구조체 안에는 여러 변수들이 존재한다. 각 멤버 변수에 접근하려면 멤버
접근 연산자인 .를 이용한다.

 strcpy(student1.name, "홍길동");

 student1.korean = 95;
 student1.english = 100;
 student1.math = 98;
 student1.average = (double) (student1.korean+student1.english+student1.math) / 3;
 
 다른 학생(student2)에 관한 변수가 필요하다면 구조체 변수를 하나 더 선언해주면 된다.
 struct grade student2;
 strcpy(student2.name, "효도르");
 student2.korean = 90;
 student2.english = 95;
 student2.math = 100;
 student2.average = (double) (student2.korean+student2.english+student2.math) / 3;

 구조체 정의와 동시에 구조체 변수를 선언하는 것도 가능하며 이때에는 구조체의 태그명이 생략 가능하지만 되도록
태그명을 지정하는 것이 좋다.
 struct gender{
 int m;
 int f;
 }g1, g2;

 struct {
 int m;
 int f;
 }g1, g2;
 
 구조체 변수도 초기화 하지 않으면 쓰레기 값을 가지므로 초기화하는 것이 좋다.
 struct grade student1 = {"홍길동", 100, 100, 100, 0.0};
 초기값이 멤버의 수보다 적으면 나머지 멤버들은 0으로 초기화 된다.
 struct grade student2 = {"효도르"}; //나머지 멤버는 모두 0으로 초기화 됨.

<예제> grade 구조체의 정의 및 사용
 #include <stdio.h>

struct grade{
 char name[10];
 int korean, english, math;
 double average;
};

int main(void){
struct grade student1 = {"홍길동", 95, 100, 98, 0.0};
struct grade student2 = {"효도르", 90, 95, 100};

student1.average = (double) (student1.korean+student1.english+student1.math) / 3;
student2.average = (double) (student2.korean+student2.english+student2.math) / 3;

printf("이름 : %s, 평균 : %5.2f\n", student1.name, student1.average);
printf("이름 : %s, 평균 : %5.2f\n", student2.name, student2.average);

return 0;
}

4. 구조체 간의 초기화 및 대입
 같은 구조체형의 변수들끼리는 서로 초기화나 대입이 가능하다.
 struct point{
int x;
int y;
};
struct point p1 = {1,2};
struct point p2 = {3,4};
struct point p3 = p1; //p3.x와 p3.y는 각각 p1.x와 p1.y로 초기화 됨. 이것을
멤버 대 멤버 초기화
struct point p4;
p4=p2; //p2.x를 p4.x에 대입하고 p2.y를 p4.y에 대입함. 이것을 멤버 대 멤버 대입

하지만 구조체 변수 간에 비교 연산은 불가능하다.
 if(p1==p2) //컴파일 에러
 printf("좌표가 같습니다.\n");

 두 구조체 변수의 값을 비교하려면 멤버 대 멤버로 비교해야 한다.
 if(p1.x==p2.x && p1.y==p2.y)
 printf("좌표가 같습니다.\n");

5. 구조체 배열
 struct grade arr[3];

struct grade arr[3];
 name korean english  math  average   name  korean  english   math   average   name  korean  english   math  average  
<-------------arr[0]---------------><-------------arr[1]-----------------><-------------arr[2]----------------->
<--------------------------------------------------------arr[3]------------------------------------------------>

구조체 배열의 메모리 할당은 위와 같이 도식화할 수 있다.

구조체 배열도 일반 배열처럼 인덱스를 이용해서 배열의 원소에 접근할 수 있다.
 for(i=0;i<3;i++)
 arr[i].average = (double) (arr[i].korean + arr[i].english + arr[i].math) /3;

구조체 배열의 초기화는 배열의 각 원소가 각각 구조체 변수이므로 다음과 같이 한다.
 struct grade arr[3] = {
{"홍길동",100,100,99};
{"효도르",94,100,99};
{"크로캅",85,90,95};
};

6. 구조체 포인터
 구조체 포인터는 구조체 변수를 가리키는 포인터이다.
 struct grade student1 = {"홍길동",100,100,99};
 struct grade *p = &student1; //구조체 포인터 선언 및 초기화

 구조체 변수에 접근하려면 간접 참조 연산자인 *를 이용하는데 *p는 구조체 변수가 되므로 멤버에 접근하려면 .연산자가
필요하다. 이때에 (*p).name과 같이 사용한다. *p.name이라고 하면 *(p.name)이라는 의미가 되므로 주의가 필요하다.
 다른 방법으로는 간접 멤버 접근 연산자인 ->연산자를 이용할 수 있다.
 (*p).name = 100;
 p->name = 100;
 (*p).name 보다는 p->name이 더 간단하여 자주 사용된다.

7. 비트필드
 구조체 정의 중에 특별한 방법으로 비트필드가 있다. 비트필드는 구조체의 멤버를 비트 단위로 사용하도록 하는 것이다.
예를 들어 시간 정보를 저장하는 구조체를 생각해보자.
 struct time{
 int hour;
 int min;
 int sec;
};
 위와 같은 time 구조체의 크기는 12바이트가 된다. 이때 비트필드를 이용하면 2바이트나 4바이트 크기의 데이터형을 비트
단위로 나눠 사용하여 크기를 줄일 수 있다.
 struct time{
 unsigned int hour : 5; //5비트에 hour 멤버를 저장함.
 unsigned int min : 6;
 unsigned int sec : 6;
};
 비트필드 정의 시에는 멤버 이름 다음에 클론(:)을 쓰고 비트 수를 적으면 된다. 전체가 17비트이므로 unsinged int 하나만
으로 time 구조체를 저장할 수 있다. 따라서 time 구조체의 크긴느 4바이트이다. 6비트로 표현 가능한 숫자가 0~63이고
분이나 초가 0~59사이의 값이므로 6비트만으로 모든 값을 표현할 수 있다. 5비트는 0~23사이의 값을 갖는다.
 그렇다면 비트필드의 메모리 할당은 어떤 식으로 될까?

                         16                                              10                                              4                                   0

                                                                                                             
                                              sec                                              min                                     hour
비트필드의 메모리 할당 도식화.

비트필드 정의 시에 중간에 일부 비트를 비워두고 멤버를 특정 비트에 할당할 수 있다.

 struct time{
 unsigned int hour : 5; //5비트에 hour 멤버를 저장함.
 unsigned int : 2;
 unsigned int min : 6;
 unsigned int : 5;
 unsigned int sec : 6;
};
위와 같이 선언하면 hour 변수를 할당하고 2비트를 비워두고 min 변수를 할당한다.

'프로그래밍 > C' 카테고리의 다른 글

malloc & free 기본 사용법  (0) 2013.07.23
typedef  (0) 2011.01.22
공용체와 열거체  (0) 2011.01.19
배열과 포인터의 관계  (0) 2010.10.09
포인터 (Pointer)  (0) 2010.10.09
선택정렬 (Selection Sort)  (0) 2010.10.09
배열 (array)  (0) 2010.10.09

+ Recent posts