54.0 공용체 사용하기
공용체는 구조체와 정의 방법이 같지만 멤버를 저장하는 방식이 다름. 구조체는 멤버들이 각각 공간을 차지하지만 공용체는 모든 멤버가 공간을 공유.
즉, 공용체는 멤버 중에서 가장 큰 자료형의 공간을 공유. 현실에서 예를 들자면 물건이 하나 들어있는 선물상자와 비슷. 같은 크기의 상자지만 들어있는 물건의 종류가 다름.
참고로 공용체는 내용이 다소 어려움. 쓸 일이 많지 않으므로 완벽히 이해하지 않아도 됨.
54.1 공용체를 만들고 사용하기
공용체는 union 키워드를 사용하여 정의
union 공용체 이름{
자료형 멤버이름;
}
공용체는 정의만 해서는 사용을 할 수가 없음 따라서 공용체도 변수로 선언해서 사용함
-union 공용체이름 변수이름;
#define _CRT_SECURE_NO_WARNINGS //strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h> //strcpy 함수가 선언된 헤더 파일
union Box { //공용체 정의
short candy; //2byte
float snack; //4byte
char doll[8]; //8byte
};
int main()
{
union Box b1; //공용체 변수 선언
printf("%d\n", sizeof(b1)); //8 : 공용체의 전체 크기는 가장 큰 자료형의 크기
strcpy(b1.doll, "bear");
printf("%d\n", b1.candy); // 25954
printf("%f\n", b1.snack); // 4464428256607938511036928229376.000000
printf("%s\n", b1.doll); // bear
return 0;
}
공용체는 보통 main 함수 바깥에 정의
만약 함수 안에서 공용체를 정의하면 해당 함수 안에서만 공용체를 사용할 수 있음
공용체의 크기 구하기
-sizeof(union 공용체)
-sizeof(공용체변수)
-sizeof 공용체 변수
구조체와는 달리 공용체는 멤버 중에서 가장 큰 자료형의 공간을 공유
따라서 어느 한 멤버에 값을 저장하면 나머지 멤버의 값은 사용할 수 없는 상태가 됨
그래서 공용체의 멤버는 한 번에 하나씩 쓰면 값을 정상적으로 사용할 수 있음.
b1.candy = 10;
printf("%d\n, b1.candy); // 10: 사용(출력)
b1.snack = 60000.0f;
printf("%f\n", b1.snack); // 60000.00000000: 사용(출력)
strcpy(b1.doll, "bear");
printf("%s\n", b1.doll); // bear: 사용(출력)
만약 b1.candy, b1.snack, b1.doll을 구조체로 만들었다면 구조체의 전체 크기는 2 + 4 + 8 = 14바이트(구조체 멤버 정렬이 되면 16바이트). 공용체로 멤버를 한 번에 하나씩만 쓰는 상황이라면 크기는 8바이트이므로 6바이트 이득. 실무에서는 공용체에 값을 저장할 때 어떤 멤버를 사용할 것인지 미리 정해놓고, 꺼낼 때도 정해놓은 멤버에서 값을 꺼내는 식으로 사용. 즉, 선물상자 바깥에 어떤 물건이 들어있는지 적어놓고 사용하는 식.
정리하자면 공용체는 여러 멤버에 동시에 접근하지 않는 경우 같은 메모리 레이아웃에 멤버를 모아둘 때 사용. 특히 공용체는 임베디드 시스템이나 커널 모드 디바이스 드라이버 등에서 주로 사용하며 보통은 거의 쓰지 않음.
54.2 공용체와 엔디언
#include <stdio.h>
union Data { //공용체 정의
char c1;
short num1;
int num2;
};
int main()
{
union Data d1; // 공용체 변수 선언
d1.num2 = 0x12345678; // 리틀 엔디언에서는 메모리에 저장될 때 78 56 34 12로 저장됨
printf("0x%x\n", d1.num2); // 0x12345678: 4바이트 전체 값 출력
printf("0x%x\n", d1.num1); // 0x5678: 앞의 2바이트 값만 출력
printf("0x%x\n", d1.c1); // 0x78: 앞의 1바이트 값만 출력
printf("%d\n", sizeof(d1)); // 4: 공용체의 전체 크기는 가장 큰 자료형의 크기
return 0;
}
공용체 변수 d1의 멤버 중에서 가장 큰 자료형인 num2에 0x12345678을 할당
printf로 d1.num2, d1.num1, d1.c1을 출력해보면 d1.num2는 저장한 숫자가 그대로 나오지만 다른 멤버는 숫자의 일부분만 나옴
공용체는 값을 저장하는 공간은 공유하지만 값을 가져올 때는 해당 자료형의 크기만큼 가져오기 때문
그런데 앞의 값만 나와야 한다면 0x1234와 0x12가 나와야 하는데 왜 0x5678, 0x78이 나오는 걸까?
우리가 사용하는 x86(x86-64) 계열 CPU는 리틀 엔디언이라는 방법으로 값을 메모리에 저장
간단하게 이야기하면 리틀 엔디언은 숫자를 1바이트씩 쪼개서 낮은 자릿수가 앞에 옴.
사람이 보기에는 반대로 뒤집혀 있음.
0x12345678을 리틀 엔디언 방식으로 메모리에 저장하면 78 56 34 12가 됨.
공용체는 앞에서부터 자료형의 크기만큼 값을 가져오게 되므로 d1.num1은 앞의 2바이트 56 78을 가져오게 되고, d1.c1은 앞의 1바이트 78만 가져오게 되는 것(저장할 때 뒤집혀서 저장되었으므로 가져올 때는 다시 되돌려서 가져옵니다. 따라서 78 56이 아니라 56 78이 됨)
공용체도 구조체와 마찬가지로 typedef로 별칭을 지정하고, 익명 공용체를 정의할 수 있음.
먼저 공용체 별칭은 다음과 같이 정의
typedef union 공용체이름 {
자료형 멤버이름;
} 공용체별칭;
typedef로 공용체의 별칭을 만들었다면 변수는 다음과 같이 선언
- 공용체 별칭 변수이름;
익명공용체
typedef union { // 익명 공용체 정의
short candy;
float snack;
char doll[8];
} Box; // typedef를 사용하여 공용체 별칭을 Box로 정의
Box b1; // 공용체 별칭으로 공용체 변수 선언
참고 | 공용체를 정의하는 동시에 변수 선언하기
union 공용체이름 {
자료형 멤버이름;
} 변수;
예)
union Box { // 공용체 정의
short candy;
float snack;
char doll[8];
} b1; // 공용체를 정의하는 동시에 변수 b1 선언
54.3 공용체 포인터를 선언하고 메모리 할당하기
- union 공용체이름 *포인터이름 = malloc(sizeof(union 공용체이름));
#define _CRT_SECURE_NO_WARNINGS
#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)); // 8: 공용체의 전체 크기는 가장 큰 자료형의 크기
strcpy(b1->doll, "bear");
printf("%d\n", b1->candy); // 25954
printf("%f\n", b1->snack); // 4464428256607938511036928229376.000000
printf("%s\n", b1->doll); // bear
free(b1);
return 0;
}
참고 | 공용체 별칭과 익명 공용체 포인터에 메모리 할당하기
typedef로 정의한 공용체 별칭으로도 포인터를 선언하고 메모리를 할당할 수 있음.
-공용체별칭*포인터이름=malloc(sizeof(공용체별칭));
typedef union _Box { // 공용체 이름은 _Box
short candy;
float snack;
char doll[8];
} Box; // typedef를 사용하여 공용체 별칭을 Box로 정의
Box *b1 = malloc(sizeof(Box)); // 공용체 포인터 선언, 메모리 할당
공용체 포인터에 메모리를 할당하지 않고, 공용체 변수를 그대로 활용할 수 있음.
-공용체포인터 = &공용체변수;
union Box {
short candy;
float snack;
char doll[8];
};
int main()
{
union Box b1; // 공용체 변수 선언
union Box *ptr; // 공용체 포인터 선언
ptr = &b1; // b1의 메모리 주소를 구하여 ptr에 할당
strcpy(ptr->doll, "bear"); // doll에 문자열 bear 복사
printf("%d\n", ptr->candy); // 25954
printf("%f\n", ptr->snack); // 4464428256607938511036928229376.000000
printf("%s\n", ptr->doll); // bear
return 0;
}
'C > C언어 코딩도장' 카테고리의 다른 글
UNIT 56 구조체 비트 필드 사용하기 (0) | 2022.03.13 |
---|---|
UNIT 55 구조체와 공용체 활용하기 (0) | 2022.03.13 |
UNIT 53 구조체 배열 사용하기 (0) | 2022.03.12 |
UNIT 52 구조체와 메모리 활용하기 (0) | 2022.03.11 |
UNIT 51 구조체 멤버 정렬 사용하기 (0) | 2022.03.11 |