# 구조체의 정의와 typedef 선언

# `typedef` 선언

`typedef` 선언은 기존에 존재하는 자료형의 이름에 새 이름을 부여하는 것을 목적으로 하는 선언

```c
// 예를 들어서 다음과 같이 typedef 선언을 하게 되면,
typedef int INT; // int의 또 다른 이름 INT를 부여!
/*
이로 인해 다음의 뜻을 컴파일러에게 전달
"자료형의 이름 int에 INT라는 이름을 추가로 붙여줍니다"
따라서 위의 선언 이후로는 다음의 형태로 int형 변수를 선언할 수 있다.
*/
INT num;  // int num; 과 동일한 선언
INT * ptr;  // int * ptr; 과 동일한 선언
```

```c
#include <stdio.h>

typedef int INT;
typedef int * PTR_INT;

typedef unsigned int UINT;
typedef unsigned int * PTR_UINT;

typedef unsigned char UCHAR;
typedef unsigned char * PTR_UCHAR;

int main(void)
{
    INT num1 = 120;     // int num1 = 120;
    PTR_INT pnum1 = &num1;      // int * pnum1 = &num1;

    UINT num2 = 190;        // unsinged int num2 = 190;
    PTR_UINT pnum2 = &num2;     // unsigend int * pnum2 = &num2;

    UCHAR ch = 'Z';     // unsigned char ch = 'Z';
    PTR_UCHAR pch = &ch;        // unsigned char * pch = &ch;

    printf("%d, %u, %c\n", *pnum1, *pnum2, *pch);
    return 0;
}

/* 결과
120, 190, Z
*/
```

## 구조체의 정의와 `typedef` 선언

```c
// 구조체 변수의 선언에 있어서 struct 선언을 생략할 수 있는 방법
struct point
{
    int xpos;
    int ypos;
};

typedef struct point Point;  // struct point에 Point라는 이름을 부여!
// 이는 struct point를 대신할 수 있도록 Point라는 이름을 정의한 셈이니, 이후로는 다음과 같이 struct 선언을 생략한 형태로 구조체 변수를 선언할 수 있다
Point pos;
// 때문에 구조체의 정의와 typedef의 선언을 한 데 묶을 수 있고, 이게 일반적인 선언 방식이다.
typedef struct point
{
    int xpos;
    int ypos;
} Point;
```

```c
#include <stdio.h>

struct point
{
    int xpos;
    int ypos;
};

typedef struct point Point;

typedef struct person
{
    char name[20];
    char phoneNum[20];
    int age;
} Person;

int main(void)
{
    Point pos={10, 20};
    Person man={"이승기", "010-1212-0001", 21};
    printf("%d %d\n", pos.xpos, pos.ypos);
    printf("%s %s %d\n", man.name, man.phoneNum, man.age);
    return 0;
}

/* 결과
10 20
이승기 010-1212-0001 21
*/
```

## 구조체의 이름 생략

```c
// 앞서 보인 예제에서는 구조체 person을 다음의 형태로 정의
typedef struct person
{
    char name[20];
    char phoneNum[20];
    int age;
} Person;
/*
이렇게 정의가 되면, 구조체의 이름 person은 사실상 별 의미를 갖지 않게 됌
따라서, 다음과 같이 구조체의 이름을 생략하는 것도 가능
*/
typedef struct
{
   char name[20];
   char phoneNum[20];
   int age;
} Person;
```

---

# 함수로의 구조체 변수 전달과 반환

## 함수의 인자로 전달되고 `return`문에 의해 반환되는 구조체 변수

```c
void SimpleFunc(int num) { . . . . }
int main(void)
{
    int age=24;
    SimpleFunc(age);  // age에 저장된 값이 매개뱐수 num에 전달(복사)
    . . . .
}
```

```c
#include <stdio.h>

typedef struct 
{
    int xpos;
    int ypos;
} Point;

void ShowPosition(Point pos)
{
    printf("[%d, %d]\n", pos.xpos, pos.ypos);
}

Point GetCurrentPosition(void)
{
    Point cen;
    printf("Input current pos: ");
    scanf("%d %d", &cen.xpos, &cen.ypos);
    return cen;
}

int main(void)
{
    Point curPos=GetCurrentPosition();
    ShowPosition(curPos);
    return 0;
}

/* 결과
Input current pos: 2 4
[2, 4]
*/
```

```c
#include <stdio.h>

typedef struct
{
    char name[20];
    char phoneNum[20];
    int age;
} Person;

void ShowPersonInfo(Person man)
{
    printf("name: %s\n", man.name);
    printf("phone: %s\n", man.phoneNum);
    printf("age: %d\n", man.age);
}

Person ReadPersonInfo(void)
{
    Person man;
    printf("name? "); scanf("%s", man.name);
    printf("phone? "); scanf("%s", man.phoneNum);
    printf("age? "); scanf("%d", &man.age);
    return man;
}

int main(void)
{
    Person man=ReadPersonInfo();
    ShowPersonInfo(man);
    return 0;
}

/* 결과
name? Jung
phone? 010-12XX-34XX
age? 22
name: Jung
phone: 010-12XX-34XX
age: 22
*/
```

```c
#include <stdio.h>

typedef struct
{
    int xpos;
    int ypos;
} Point;

void OrgSymTrans(Point * ptr)       // 원점대칭
{
    ptr->xpos = (ptr->xpos) * -1;
    ptr->ypos = (ptr->ypos) * -1;
}

void ShowPosition(Point pos)
{
    printf("[%d, %d]\n", pos.xpos, pos.ypos);
}

int main(void)
{
    Point pos={7, -5};
    OrgSymTrans(&pos);      // pos의 값을 원점 대칭이동시킨다.
    ShowPosition(pos);
    OrgSymTrans(&pos);      // pos의 값을 원점 대칭이동시킨다.
    ShowPosition(pos);
    return 0;
}

/* 결과
[-7, 5]
[7, -5]
*/
```

## 구조체 변수를 대상으로 가능한 연산

구조체 변수를 대상으로는 매우 제한된 형태의 연산만 허용

허용되는 가장 대표적인 연산은 대입연산이며, 그 이외로 주소 값 반환을 목적으로 하는 `&` 연산이나 구조체 변수의 크기를 반환하는 `sizeof` 정도의 연산만 허용

```c
#include <stdio.h>

typedef struct
{
    int xpos;
    int ypos;
} Point;

int main(void)
{
    Point pos1={1, 2};
    Point pos2;
    pos2=pos1;      // pos1의 멤버 대 pos2의 멤버 간 복사가 진행됨

    printf("크기: %lu\n", sizeof(pos1));     // pos1의 전체 크기 반환
    printf("[%d, %d]\n", pos1.xpos, pos1.ypos);
    printf("크기: %lu\n", sizeof(pos2));     // pos2의 전체 크기 반환
    printf("[%d, %d]\n", pos2.xpos, pos2.ypos);
    return 0;
}

/* 결과
크기: 8
[1, 2]
크기: 8
[1, 2]
*/
```

구조체 안에는 배열도 존재할 수 있고 포인터 변수도 존재할 수 있다. 뿐만 아니라 다른 구조체의 변수도 존재할 수 있다.

따라서 구조체 변수 대상의 덧셈과 뺄셈을 하려면 함수를 정의해야 한다.

```c
#include <stdio.h>

typedef struct 
{
    int xpos;
    int ypos;
} Point;

Point AddPoint(Point pos1, Point pos2)
{
    Point pos={pos1.xpos+pos2.xpos, pos1.ypos+pos2.ypos};
    return pos;
}

Point MinPoint(Point pos1, Point pos2)
{
    Point pos={pos1.xpos-pos2.xpos, pos1.ypos-pos2.ypos};
    return pos;
}

int main(void)
{
    Point pos1={5, 6};
    Point pos2={2, 9};
    Point result;

    result = AddPoint(pos1, pos2);
    printf("[%d, %d]\n", result.xpos, result.ypos);
    result = MinPoint(pos1, pos2);
    printf("[%d, %d]\n", result.xpos, result.ypos);
    return 0;
}

/* 결과
[7, 15]
[3, -3]
*/
```

---

# 구조체의 유용함에 대한 논의와 중첩 구조체

## 구조체를 정의하는 이유

### “구조체를 통해서 연관 있는 데이터를 하나로 묶을 수 있는 자료형을 정의하면, 데이터의 표현 및 관리가 용이해지고, 그만큼 합리적인 코드를 작성할 수 있게 된다.”

```c
#include <stdio.h>

typedef struct 
{
    char name[20];      // 학생 이름
    char stdnum[20];    // 학생 고유번호
    char school[20];    // 학교 이름
    char major[20];     // 선택 전공
    int year;           // 학년
} Student;

void ShowStudentInfo(Student * sptr)
{
    printf("학생 이름: %s\n", sptr->name);
    printf("학생 고유번호: %s\n", sptr->stdnum);
    printf("학교 이름: %s\n", sptr->school);
    printf("선택 전공: %s\n", sptr->major);
    printf("학년: %d\n", sptr->year);
}

int main(void)
{
    Student arr[7];
    int i;

    for(i=0 ; i<7 ; i++)
    {
        printf("이름: "); scanf("%s", arr[i].name);
        printf("번호: "); scanf("%s", arr[i].stdnum);
        printf("학교: "); scanf("%s", arr[i].school);
        printf("전공: "); scanf("%s", arr[i].major);
        printf("학년: "); scanf("%d", &arr[i].year);
    }

    for(i=0 ; i<7 ; i++)
        ShowStudentInfo(&arr[i]);
    return 0;
}
```

## 중첩된 구조체의 정의와 변수의 선언

### 구조체 안에 구조체 변수가 멤버로 존재하는 경우를 가리켜 ‘구조체의 중첩’이라 한다.

```c
#include <stdio.h>

typedef struct 
{
    int xpos;
    int ypos;
} Point;

typedef struct 
{
    Point cen;
    double rad;
} Circle;

void ShowCircleInfo(Circle * cptr)
{
    printf("[%d, %d]\n", (cptr->cen).xpos, (cptr->cen).ypos);
    printf("radius: %g \n\n", cptr->rad);
}

int main(void)
{
    Circle c1={{1, 2}, 3.5};
    Circle c2={2, 4, 3.9};
    ShowCircleInfo(&c1);
    ShowCircleInfo(&c2);
    return 0;
}

/* 결과
[1, 2]
radius: 3.5 

[2, 4]
radius: 3.9
*/
```

---

# 공용체(Union Type)의 정의와 의미

## 구조체 vs 공용체

```c
typedef struct sbox  // 구조체 sbox의 정의
{
    int mem1;
    int mem2;
    double mem3;
} SBox;

typedef union ubox  // 공용체 ubox의 정의
{
    int mem1;
    int mem2;
    double mem3;
} UBox;
/* 위의 구조체와 공용체를 대상으로 각각 다음 연산을 하면, */
printf("%d \n", sizeof(SBox));  // 16 출력
printf("%d \n", sizeof(UBox));  // 8 출력
/* 여기서 16은 모든 멤버의 크기를 합한 결과이고, 8은 멤버 중에서 가장 크기가 큰 double의 크기만 계산된 결과 */
```

```c
#include <stdio.h>

typedef struct sbox     // 구조체 sbox의 정의
{
    int mem1;
    int mem2;
    double mem3;
} SBox;

typedef union ubox      // 공용체 ubox의 정의
{
    int mem1;
    int mem2;
    double mem3;
} UBox;

int main(void)
{
    SBox sbx;
    UBox ubx;
    printf("%p %p %p\n", &sbx.mem1, &sbx.mem2, &sbx.mem3);
    printf("%p %p %p\n", &ubx.mem1, &ubx.mem2, &ubx.mem3);
    printf("%lu %lu\n", sizeof(SBox), sizeof(UBox));
    return 0;
}

/* 결과
0x16f5a2f58 0x16f5a2f5c 0x16f5a2f60
0x16f5a2f50 0x16f5a2f50 0x16f5a2f50
16 8
UBox형 변수를 구성하는 멤버 mem1, mem2, mem3의 주소 값이 동일하다
공용체 변수가 선언되면, 공용체를 구성하는 멤버는 각각 할당되지 않고, 그 중 크기가 가장 큰 멤버의 변수만 하나 할당되어
공유하게 된다.
*/
```

```c
#include <stdio.h>

typedef union ubox      // 공용체 ubox의 정의
{
    int mem1;
    int mem2;
    double mem3;
} UBox;

int main(void)
{
    UBox ubx;       // 8 바이트 메모리 할당
    ubx.mem1=20;        // 상위 4 바이트의 메모리 공간에 20을 저장
    printf("%d\n", ubx.mem2);
    /* mem2는 int형 변수이므로 이 이름으로 접근할 경우 상위 4바이트의 메모리 공간을 참조하게 된다. 그런데 앞서 이 공간에 20을 저장했으므로 20이 출력*/
    ubx.mem3=7.15;
    /* mem3에 실수를 저장, 결과적으로 13행을 통해 저장된 값을 덮어써버리게 됌 */
    printf("%d\n", ubx.mem1);
    printf("%d\n", ubx.mem2);
    /* 실수를 저장하면서 덮어버렸기 때문에, 상위 4 바이트를 읽어서 출력하면 알 수 없는 값이 출력 */
    printf("%g\n", ubx.mem3);
    return 0;
}

/* 결과
20
-1717986918
-1717986918
7.15
*/
```

## 공용체의 유용함은 다양한 접근방식을 제공하는 데 있습니다.

### 공용체의 유용함은 ‘하나의 메모리 공간을 둘 이상의 방식으로 접근할 수 있다'

```c
/* 상위 2 바이트와 하위 2 바이트 값을 양의 정수, 상위 1 바이트와 하위 1 바이트에 저장된 값의 아스키 문자 */
#include <stdio.h>

typedef struct
{
    unsigned short upper;
    unsigned short lower;
} DBShort;

typedef union
{
    int iBuf;
    char bBuf[4];
    DBShort sBuf;
} RDBuf;

int main(void)
{
    RDBuf buf;
    printf("정수 입력: ");
    scanf("%d", &(buf.iBuf));

    printf("상위 2 바이트: %u\n", buf.sBuf.upper);
    printf("하위 2 바이트: %u\n", buf.sBuf.lower);
    printf("상위 1 바이트 아스키 코드: %c\n", buf.bBuf[0]);
    printf("하위 1 바이트 아스키 코드: %c\n", buf.bBuf[3]);
    return 0;
}

/* 결과
정수 입력: 1145258561
상위 2 바이트: 16961
하위 2 바이트: 17475
상위 1 바이트 아스키 코드: A
하위 1 바이트 아스키 코드: D

공용체의 적절한 정의를 통해서 4 바이트 메모리 공간을 2 바이트씩, 그리고 1 바이트씩 접근할 수 있다.
*/
```

---

# 열거형의 정의와 의미

## 열거형의 정의와 변수의 선언

```c
/* 열거형으로 syllable이라는 이름의 자료형을 정의한다는 것은 다음의 의미를 지닌다. 
"syllable형 변수에 저장이 가능한 정수 값들을 결정하겠다!" 
열거형의 경우에는 저장이 가능한 값 자체를 정수의 형태로 결정 */
enum syllable  // syllable이라는 이름의 열거형 정의
{
    Do=1, Re=2, Mi=3, Fa=4, So=5, La=6, Ti=7
};
/*
"Do를 정수 1을 의미하는 상수로 정의, 그리고 이 값은 syllable형 변수에 저장이 가능"
*/
```

```c
#include <stdio.h>

typedef enum syllable       // typedef 선언이 추가된 열거형의 정의
{
    Do=1, Re=2, Mi=3, Fa=4, So=5, La=6, Ti=7
} Syllable;

void Sound(Syllable sy)
{
    switch(sy)
    {
        case Do:
            puts("도는 하얀 도라지"); return;
        case Re:
            puts("레는 둥근 레코드"); return;
        case Mi:
            puts("미는 파란 미나리"); return;
        case Fa:
            puts("파는 예쁜 파랑새"); return;
        case So:
            puts("솔은 작은 솔방울"); return;
        case La:
            puts("라는 라디오고요~"); return;
        case Ti:
            puts("시는 졸졸 시냇물"); return;
    }
    puts("다 함께 부르세~ 도레미파 솔라시도 홀 도~ 짠~");
}

int main(void)
{
    Syllable tone;
    for(tone=Do ; tone<=Ti ; tone++)
        Sound(tone);
    return 0;
}

/* 결과
도는 하얀 도라지
레는 둥근 레코드
미는 파란 미나리
파는 예쁜 파랑새
솔은 작은 솔방울
라는 라디오고요~
시는 졸졸 시냇물

위 반복문에서 Do와 Ti는 각각 1과 7을 의미하는 상수이니
for(tone=1 ; tone<=7 ; tone++)
로 대신해도 그 결과는 동일하다.
*/
```

## 열거형 상수의 값이 결정되는 방식

### 열거형을 정의하는데 있어서 상수의 값을 명시하지 않으면

```c
enum color {RED, BLUE, WHITE, BLACK};
/* 위 정의에는 상수의 이름만 선언되었을 뿐 값은 선언되어 있지 않다. 이러한 경우 열거형 상수의 값은 0에서부터 1씩 증가하는 형태로 결정 */
enum color {RED=0, BLUE=1, WHITE=2, BLACK=3}; // 위와 동일
/* 이번에는 정의 형태가 조금 다른 예를 보자. */
enum color {RED=3, BLUE, WHITE=6, BLACK};
/* 값이 선언되지 않으면 앞서 선언된 상수보다 1이 증가된 값이 할당 */
enum color {RED=3, BLUE=4, WHITE=6, BLACK=7}; // 위와 동일
```

## 열거형의 유용함은 이름있는 상수의 정의를 통한 의미의 부여에 있습니다.

### “Do, Re, Mi와 같이 연관이 있는 이름을 동시에 상수로 선언할 수 있다.”

### “둘 이상의 연관이 있는 이름을 상수로 선언함으로써 프로그램의 가독성을 높이는데 있다.”
