1. 정의
 typedef 기존데이터형 새이름;
 
 ex) typedef int NUM;
       typedef char STR;
       typedef struct STUDENT;

typedef는 매번 앞에 struct나 union, enum 키워드를 써야하는 구조체나 공용체, 열거체를 정의할 때 유용하다. 'struct 태그명'을 typedef로 이름을 지정하면 사용하기가 편리하다.

  typedef struct point{
   int x;
   int y;
 }POINT; // struct point를 POINT라는 이름으로 정의

위와 같이 struct point를 POINT라고 정의하면 POINT를 새로운 데이터형으로 사용할 수 있다.

 POINT p1, p2; // 이것은 struct point p1, p2;와 같은 의미이다.

수학에서 치환과 비슷한 개념이라고 생각하면 된다.
위와 같이 구조체를 정의하면서 typedef를 정의할 수도 있고, 구조체를 정의한 후에 할 수도 있다.

  struct point{
   int x;
   int y;
 };
 typedef struct point POINT;

typedef로 정의된 데이터형도 원형 그대로 사용이 가능하다.

 struct point p1;
 POINT p2;

<예제>
 #include <stdio.h>
 struct point{
  int x;
  int y;
 };
 typedef struct point POINT;

 int main(void){
   struct point p1 = {10, 20};
   POINT p2;
   p2 = p1;
   printf("p2의 좌표 : %d, %d\n', p2.x, p2.y);
   return 0;
}

2. typedef 사용 목적
 typedef를 사용하면 이식성(portability)과 가독성(readability)에 큰 도움이 된다.
  struct point{
   int x;
   int y;
 }; // 1번

  struct point{
   double x;
   double y;
 }; // 2번

 만약 point 구조체가 1번과 같을 때 변수는 int형이다. 그런데 만약 실수를 사용하는 경우가 생긴다면 2번과 같이 구조체를 새로 정의해야 한다. point 구조체를 사용하는 함수가 존재하거나 다른 곳에서 자주 사용되고 있다면 일일이 모두 수정해야만 한다. 하지만 typedef를 사용하면 간단하다.

 typedef int nType;
 struct point{
   nType x;
   nType y;
 }; // 1번

 typedef double nType;
 struct point{
   nType x;
   nType y;
 }; // 2번
 typedef 정의 부분에서 데이터형만 변경함으로써 간단하게 수정이 가능하다.

 가독성 면에선 다음과 같은 경우를 가정해보자. unsigned char는 문자를 저장할 수도 있고 1바이트 정수형을 저장할 수도 있다. 이 경우 어떤 용도인지 명확히 구분할 때 typedef를 사용할 수 있다.
 typedef unsigned char oneByte;
 oneByte data;

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

assert 매크로 사용법  (0) 2014.01.27
이중배열과 포인터 문제 하나  (0) 2013.12.01
malloc & free 기본 사용법  (0) 2013.07.23
공용체와 열거체  (0) 2011.01.19
구조체  (0) 2010.10.23
배열과 포인터의 관계  (0) 2010.10.09
포인터 (Pointer)  (0) 2010.10.09
1. 공용체
 공용체는 여러 멤버들이 메모리를 공유하게 하는 것이다. 즉, 모든 멤버들의 주소가 같다. 구조체는 메모리에
순차적으로 멤버를 할당하지만 공용체는 같은 메모리를 함께 사용하는 이다.

union 태그명{
 데이터형 멤버형;
 데이터형 멤버형;
 ...
};

ex)
union data{
 unsigned int num;
 unsigned char arr[4];
}; 

 union data d1; //공용체 변수의 선언

 공용체 변수는 메모리에 할당될 때 같은 주소에 할당되는데 공용체 크기는 가장 큰 멤버의 크기와 같다.
위의 예에서는 멤버 모두 4바이트이므로 전체 크기도 4바이트가 된다.
<--------------------------------------------------- num --------------------------------------------------->
 arr[0] arr[1]  arr[2]  arr[3] 
<-------------------------------------------------- 4바이트-------------------------------------------------->

 공용체의 초기화나 접근 방법은 구조체와 동일하게 {}와 .을 사용한다. 다만 초기화할 때 여러 멤버 중 한 번에
하나만 사용할 수 있으므로 첫 번째 멤버의 초기값만 지정한다.

<예제> 공용체의 사용 예
 #include <stdio.h>
union data{
 unsigned int num;
 unsigned char arr[4];
};
int main(void){
union data d1;
int i;
printf("data 공용체의 크기 : %d\n", sizeof(union data));
d1.num = 0x12345678;
printf("d1.num = %08x\n", d1.num);
for(i=0;i<4;i++)
 printf("d1.arr[%d] = %02x\n", i, d1.arr[i]);
return 0;
}

d1.num을 0x12345678로 초기화했기 때문에 같은 메모미를 사용하는 d1.arr 역시 같은 값을 가진다. d1.arr는 배열이므로
4바이트인 num의 값을 바이트 단위로 접근이 가능하다. 따라서 실행결과는 다음과 같다.

 data 공용체의 크기 : 4
d1.num = 12345678
d1.arr[0] = 78
d1.arr[1] = 56
d1.arr[2] = 34
d1.arr[3] = 12

※ 리틀 엔디안과 빅 엔디안
리틀 엔디안(little endian) : 최하위 바이트부터 메모리에 저장하는 방식 (인텔 계열 CPU)
빅 엔디안(big endian) : 최상위 바이트부터 메모리에 저장하는 방식 (모토로라 계열 CPU)

                                                                     unsugned int num = 0x12345678;

 12  34 56  78    78  56  34  12 
0x1000          0x1001           0x1002          0x1003                                0x1000          0x1001           0x1002          0x1003
                             빅 엔디안 방식                                                                            리틀 엔디안 방식

PC는 주로 인텔 계열의 CPU를 사용하므로 예제의 결과도 리틀 엔디안 방식으로 메모리에 값이 저장된 것을 확인할 수 있다.

공용체가 유용하게 사용되는 경우를 살펴보자.

 struct info{
 char name[10];
 int job_code;
 char company_name[20];
 char school_name [20];
};
job_code가 0이면 학생, 1이면 회사원이라고 가정하자. 이때 위와 같이 구조체로 정의하면 job_code가 어떤 것이라도
일단 메모리에 company_name 변수와 school_name 변수가 할당된다. 따라서 학생일 경우 company_name 변수를
사용하지 않고 회사원일 경우 school_name 변수를 사용하지 않으므로 메모리가 낭비된다.
이런 경우 공용체를 이용해서 구조체를 정의하면 메모리 낭비를 막을 수 있다.

 struct info{
 char name[10];
 int job_code;
union{
 char company_name[20];
 char school_name [20];
}job_info;
};

2. 열거체
열거체(enumerated type)는 열거형이라고도 하며, 정수형의 일종이다.

 enum 태그명 {열거상수1, 열거상수2, ...};

ex) enum week {sun, mon, tue, wed, thu, fri, sat};
 위와 같이 열거체 변수에는 열거체 정의에 나열된 열거상수 값 중 하나를 저장해야 한다.
 열거체와 열거상수는 프로그램의 가독성(readability)을 향상시키는 기능을 한다.


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

이중배열과 포인터 문제 하나  (0) 2013.12.01
malloc & free 기본 사용법  (0) 2013.07.23
typedef  (0) 2011.01.22
구조체  (0) 2010.10.23
배열과 포인터의 관계  (0) 2010.10.09
포인터 (Pointer)  (0) 2010.10.09
선택정렬 (Selection Sort)  (0) 2010.10.09
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