이 페이지 요약 

1. 공용체 

2. 리틀 엔디언

3. 구조체, 공용체 함께 사용

 

Unit 54. 공용체 사용하기

공용체는 다음 그림과 같이 모든 멤버가 공간을 공유한다.

공용체는 멤버중에 가장 큰 자료형의 공간을 공유한다.

54.1 공용체를 만들고 사용하기

공용체는 union 키워드를 사용하여 정의한다. 공용체는 다음과 같이 정의하고, 변수로 선언해서 사용한다.

union 공용체이름 {
    자료형 멤버이름;
};

union 공용체이름 변수이름;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <string.h>
 
union Box{
    short candy;
    float snack;
    char doll[8];
};
 
int main()
{
    union Box b1;
    printf("%d\n"sizeof(b1));
 
    strcpy(b1.doll, "bear");
    
    printf("%d\n", b1.candy);
    printf("%f\n", b1.snack);
    printf("%s\n", b1.doll);
    return 0;
}
cs

union 키워드 뒤에 공용체 이름을 지정하고, 중괄호 안에 변수를 선언한다. 닫는 중괄호 뒤에는 반드시 세미클론을 붙여줘야 한다. 공용체는 보통 main함수 밖에 정의한다.

정의한 공용체를 사용하려면 공용체 변수를 선언해야 한다. 공용체 이름 앞에는 union 키워드를 붙이고, 공용체 이름과 공용체 변수 이름을 지정하여 선언한다. sizeof 연산자로 공용체 크기를 구하면 가장 큰 자료형의 크기가 공용체의 전체 크기로 나온다. 위 코드의 경우 char 8개 크기인 배열로 8바이트 이다.

공용체의 멤버에 접근할 때는 점을 사용한다. 공용체는 멤버중 가장 큰 자료형의 공간을 공유하기 때문에 어느 한 멤버에 값을 저장하면 나머지 멤버의 값은 사용할 수 없는 상태가 된다. 공용체의 멤버는 한 번에 하나씩 값을 쓰면 정상적으로 사용할 수 있다.

실무에서는 공용체에 값을 저장할 때 어떤 멤버를 사용할 지 미리 정해두고 꺼낼때도 정해둔 멤버에서 값을 꺼내는 식으로 사용한다. 여러 멤버에 동시에 접근하지 않는 경우 메모리 레이아웃에 멤버를 모아둘 때 사용한다. 임베디드 시스템이나 커널 모드 디바이스 드라이버 등에서 주로 사용하며 보통은 거의 쓰지 않는다.

54.2 공용체와 엔디언

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
 
union Data {
    char c1;
    short num1;
    int num2;
};
 
int main()
{
    union Data d1;
 
    d1.num2 = 0x12345678;
 
    printf("0x%x\n", d1.num2);
    printf("0x%x\n", d1.num1);
    printf("0x%x\n", d1.c1);
    printf("%d\n"sizeof(d1));
    return 0;
}
cs

d1의 멤버중 가장 큰 자료형인 num2에 0x12345678을 할당하고 다른 멤버들을 출력해보면 d1.num2는 저장한 숫자가 그대로 나오지만, 다른 멤버는 숫자중 일부만 나온다. 공용체는 값을 저장하는 공간은 공유하지만 가져올 때는 해당 자료형의 크기만큼만 가져오기 때문이다. 우리가 사용하는 CPU는 리틀 엔디언 방식으로 값을 메모리에 저장한다. 리틀 엔디언은 숫자를 1바이트씩 쪼개서 낮은 자릿수가 앞에 오도록 한다. 따라서 0x12345678을 리틀 엔디언 방식으로 메모리에 저장하면 78 56 34 12가 된다. 공용체는 앞에서부터 자료형의 크기만큼 값을 가져오므로 d1.num1은 앞의 2바이트 56 78을 가져오고, d1.c1은 앞의 1바이트인 78만 가져온다.

공용체도 구조체처럼 typedef로 별칭을 지정하고 별칭으로 선언하거나, 익명 공용체를 정의할 수 있다. 또한 다음과 같이 정의하는 동시에 변수를 선언할 수 있다.

union 공용체이름 {
    자료형 멤버이름;
} 변수;

54.3 공용체 포인터를 선언하고 메모리 할당하기

공용체도 포인터를 선언하고, malloc함수를 사용하여 동적 메모리를 할당할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
union Box{
    short candy;
    float snack;
    char doll[8];
};
 
int main()
{
    union Box *b1 = malloc(sizeof(union Box));
 
    printf("%d\n"sizeof(union Box));
 
    strcpy(b1->doll, "bear");
 
    printf("%d\n", b1->candy);
    printf("%f\n", b1->snack);
    printf("%s\n", b1->doll);
 
    free(b1);
    return 0;
}
cs

malloc함수로 메모리를 할당할 때 크기를 알아야 하므로 sizeof(union Box)처럼 공용체의 크기를 구할 수 있다. 공용체 포인터도 멤버에 접근할 때는 화살표 연산자를 사용한다. strcpy함수로 공용체 포인터의 멤버에 접근한 뒤 문자열을 복사하고, 공용체 포인터도 멤버중 가장 큰 자료형의 공간을 공유함을 출력값을 통해 확인할 수 있다. 할당한 메모리는 사용이 끝났으면 꼭 free로 해제해줘야 한다.

54.4 퀴즈

정답은 long long int 자료형의 크기인 8 이다.

정답은 d이다.

정답은 0x1111 이다.

54.5 연습문제 : 정수 데이터 공용체 정의하기

정답은 다음 코드와 같다.

// 1.
union Data{
    char c1;
    short num1;
};

// 2. 
union Data

// 3.
d1.num1 = 0x5678;

0x5678 은 2바이트 이므로 short, 0x78은 1바이트이므로 char로 선언하고, 리틀 엔디언으로 값이 저장되기 때문에 0x5678을 num1멤버에 넣으면 위와 같은 결과가 나온다.

54.6 연습문제 : 공용체 포인터 사용하기

화살표 연산자를 사용했으므로 포인터를 선언하여 메모리를 할당해야 하기 때문에 정답은 union Data *d1 = malloc(sizeof(union Data)); 이다.

54.7 심사문제 : 정수 데이터 공용체 정의하기

정답은 다음 코드와 같다.

union Data{
    char c1;
    int num1;
};

공용체의 크기가 4가 나오므로 4바이트 크기를 갖는 자료형이 사용되야 한다.

54.8 심사문제 : 공용체 포인터 사용하기

정답은 다음 코드와 같다.

union Data *d1 = malloc(sizeof(union Data));
d1->num2 = 0x11111111;

화살표 연산자가 사용됬기 때문에 포인터를 선언한 후 메모리를 할당해야 한다. 공용체는 가장 큰 자료형의 공간을 공유하므로 가장 큰 멤버에만 0x111111111을 넣으면 된다.

Unit 55. 구조체와 공용체 활용하기

구조체 안에 구조체와 공용체가 들어갈 수 있고, 반대로 공용체 안에 구조체와 공용체가 들어갈 수도 있다.

55.1 구조체 안에서 구조체 멤버 사용하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
 
struct Phone {
    int areacode;
    unsigned long long num;
};
 
struct Person {
    char name[20];
    int age;
    struct Phone phone;
};
 
int main()
{
    struct Person p1;
 
    p1.phone.areacode = 82;
    p1.phone.num = 12345678;
 
    printf("%d %llu\n", p1.phone.areacode, p1.phone.num);
    return 0;
}
cs

구조체를 멤버로 가지려면 구조체 안에서 구조체 변수를 선언하면 된다. 위 코드에서는 Person 구조체가 Phone 구조체를 멤버로 가지고 있다. 멤버에 접근할 때는 점을 사용하여 가지고 있는 구조체에 계층적으로 접근하면 된다. 위 코드와 같이 점을 두 번 사용하면 구조체의 areacode와 num 멤버에 접근할 수 있다.

구조체 안에 구조체를 정의할 때는 무조건 안에 들어가는 구조체를 먼저 선언해줘야 한다.

만약 Phone구조체를 다른 곳에서는 쓰지 않고 특정 구조체 안에서만 쓴다면 다음과 같이 구조체 안에 구조체를 정의하는게 더 편리하다. 이때 안에 정의하는 구조체는 정의하고 반드시 변수를 선언해 줘야 한다.

struct Person {   
    char name[20];    
    int age;          
    struct Phone {   
        int areacode;                 
        unsigned long long num;    
    } phone;                       
};

구조체 변수를 선언하며 안에 있는 구조체까지 초기화 하려면 중괄호 안에 중괄호를 사용하여 다음과 같이 초기화 하면 된다.

struct 구조체이름 변수이름 = { 값1, 값2, { 값3, 값4 } };

55.2 구조체 안의 구조체 멤버에 메모리 할당하기

다음은 구조체 안에 구조체 멤버가 변수로 있는 상태에서 메모리를 할당하여 사용하는 방법이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <stdlib.h>
 
struct Phone {
    int areacode;
    unsigned long long num;
};
 
struct Person {
    char name[20];
    int age;
    struct Phone phone;
};
 
int main()
{
    struct Person *p1 = malloc(sizeof(struct Person));
 
    p1->phone.areacode = 82;
    p1->phone.num = 12345678;
 
    printf("%d %llu\n", p1->phone.areacode, p1->phone.num);
 
    free(p1);
    return 0;
}
cs

Person 구조체에 메모리를 할당하면 각 구조체의 멤버에 접근하려면 p1은 포인터이기 때문에 화살표 연산자를 사용하여 멤버에 접근하고, phone은 포인터가 아닌 일반 변수이므로 점을 사용하여 멤버에 접근한다. 구조체 포인터 사용이 끝나면 free함수로 할당된 메모리를 해제해야 한다.

다음은 구조체의 포인터를 멤버로 갖는 상황이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>
 
struct Phone {
    int areacode;
    unsigned long long num;
};
 
struct Person {
    char name[20];
    int age;
    struct Phone *phone;
};
 
int main()
{
    struct Person *p1 = malloc(sizeof(struct Person));
    p1->phone = malloc(sizeof(struct Phone));
 
    p1->phone->areacode = 82;
    p1->phone->num = 12345678;
 
    printf("%d %llu\n", p1->phone->areacode, p1->phone->num);
 
    free(p1->phone);
    free(p1);
    return 0;
}
cs

Person 구조체 안에는 구조체 포인터를 멤버로 갖고 있다. 먼저 바깥 구조체의 포인터에 메모리를 할당하고 멤버로 있는 구조체 포인터에 메모리를 할당해야 한다. 각 멤버에 접근하려면 p1은 포인터이고, 구조체 멤버 phone도 포인터이므로 화살표 연산자로 접근하면 된다. 구조체 포인터 사용이 끝나면 메모리를 해제해야 하는데, 안쪽에 있는 멤버부터 메모리를 해제해야 한다. 만약 바깥에 있는 구조체를 먼제 해제하게 되면 데이터가 사라져서 멤버에 더이상 접근할 수 없다. 멤버 포인터에 저장된 주소도 알 수 없기 때문에 해제도 할 수 없다. 그래서 ㅇㄴ쪽의 구조체 먼제 해제해야 한다.

55.3 익명 구조체와 익명 공용체 활용하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
 
struct Vector3 {
    union {
        struct {
            float x;
            float y;
            float z;
        };
        float v[3];
    };
};
 
int main()
{
    struct Vector3 pos;
 
    for(int i = 0; i < 3; i++)
    {
        pos.v[i] = 1.0f;
    }
 
    printf("%f %f %f\n", pos.x, pos.y, pos.z);
    return 0;
}
cs

제일 안에 있는 x, y, z는 float로 선언되어 있는 익명 구조체 이다. 익명 공용체가 x, y, z익명 구조체오 배열 v를 감싸고 있다. float x, y, z는 변수 3개이고, float v[3]도 배열의 요소가 3개이므로 자료형도 같고, 개수도 같으므로 같은 공간을 차지한다. 공용체로 묶어주면 x, y, z와 v[3]은 같은 공간을 공유하게 된다.

v는 배열이기 때문에 인덱스로 접근하여 값을 할당할 수 있다. 공용체에 넣었으므로 값은 같은 공간에 있기 때문에 x,y,z 멤버로도 접근할 수 있다.

 

55.4 퀴즈

정답은 d 이다.

정답은 c 이다.

정답은 b이다.

정답은 d 이다.

 

55.5 연습문제 : 게임 캐릭터 구조체 만들기

정답은 struct Stats stats 이다.

 

55.6 연습문제 : 게임 캐릭터 구조체 사용하기

화살표 연산자로 접근하고 있기 때문에 포인터로 만들어서 메모리를 할당시켜줘야 하므로 정답은 struct Champion *lux = malloc(sizeof(struct Champion)); 이다.

 

55.7 연습문제 : 장치 옵션 구조체 만들기

정답은 다음 코드와 같다.

union {
    unsigned long long option;
    struct {
        unsigned char boot[4];
        unsinged char interrupt[2];
        unsigned char bus[2];
    };
};

사용하는 공간이 총 8 바이트이기 때문에 공간을 공유하는 공용체를 8바이트로 만들면 코드에서 저장한 값들을 다 저장할 수 있다.

 

55.8 심사문제 : 게임 캐릭터 구조체 만들기

 

정답은 다음 코드와 같다.

struct Champion swain;

strcpy(swain.name, "Swain");
swain.stats.health = 463;

 

55.9 심사문제 : 게임 캐릭터 구조체 포인터 사용하기

정답은 다음 코드와 같다.

struct Champion *swain = malloc(sizeof(struct Champion));
swain->stats = malloc(sizeof(struct Stats));

strcpy(swain->name, "Swain");
swain->stats->health = 463;

 

55.10 심사문제 : 장치 옵션 구조체 만들기

정답은 다음 코드와 같다.

struct DeviceOption {
    union{
        unsigned short option;
        struct{
            unsigned char boot;
            unsigned char interrupt;
        };
    };
};

공용체는 모든 멤버의 공간을 공유하기 때문에 unsigned char이 두개 쓰였으므로 이 두개 모두 들어갈 수 있는 unsigned short 자료형을 사용하였다.

'Project H4C Study Group' 카테고리의 다른 글

[Project H4C] FTZ (8 ~11)  (0) 2021.03.27
[Project H4C] FTZ (1 ~ 7(문제오류))  (0) 2021.03.24
[Project H4C] C언어 코딩도장(13)  (0) 2021.03.23
[Project H4C] PLT, GOT  (0) 2021.03.21
[Project H4C] SFP, RET  (0) 2021.03.21

+ Recent posts