[ 공용체 ]
공용체는 구조체와 정의 방법이 같지만 멤버를 저장하는 방식이 다르다. 즉, 다음과 같이 멤버들이 각각 공간을 차지하지만 공용체는 모든 멤버가 공간을 공유한다.
즉, 공용체는 멤버 중에서 가장 큰 자료형의 공간을 공유한다. 현실에서 예를 들자면 물건이 하나 들어있는 선물상자와 비슷하다. 같은 크기의 상자지만 들어있는 물건의 종류가 다른 것 처럼.
공용체 만들고 사용하기
union 공용체이름{
자료형 멤버이름;
};
공용체는 정의만 해서 사용할 수 없고 따로 변수로 선언해서 사용해야한다.
#define _CRT_SECURE_NO_WARNINGS // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#inlcude <string.h> // strcpy 함수
union Box{
short candy; // 2바이트
float snack; // 4바이트
char doll[8]; // 8바이트
};
int main(){
union Box b1; // 공용체 변수 선언
printf("%d\n", sizeof(b1)); // 8: 공용체의 전체 크기는 가장 큰 자료형의 크기
strcpy(b1.doll, "bear"); // doll에 문자열 bear 복사
printf("%d\n", b1.candy); // 25954
printf("%f\n", b1.snack); // 4464428256607938511036928229376.000000
printf("%s\n", b1.doll); // bear
return 0;
}
# 실행 결과
8
25954
4464428256607938511036928229376.000000
bear
- printf로
b1.candy
,b1.snack
,b1.doll
의 값을 출력해보면b1.doll
은 bear가 정상적으로 출력되지만,b1.candy
와b1.snack
은 값이 엉망이 되었다. - 구조체와는 달리 공용체는 멤버 중에서 가장 큰 자료형의 공간을 공유한다. 따라서 어느 한 멤버에 값을 저장하면 나머지 멤버의 값은 사용할 수 없는 상태가 된다.
- 그래서 공용체의 멤버는 한 번에 하나씩 쓰면 값을 정상적으로 사용할 수 있다.
- 만약
b1.candy/snack/doll
을 구조체로 만들었다면 구조체의 전체크기는 2+4+8 = 14바이트이다. - 공용체로 멤버를 한 번에 하나씩만 쓰는 상황이라면 크기는 8바이트이므로 6바이트 이득인것이다.
- 실무에서는 공용체에 값을 저장할 때 어떤 멤버를 사용할 것인지 미리 정해놓고, 꺼낼때도 정해놓은 멤버에서 값을꺼내는 식으로 사용한다. 즉, 선물 상자 바깥에 어떤 물건이 들어있는지 적어놓고 사용하는 식이다.
=> 정리하자면 공용체는 여러 멤버에 동시에 접근하지 않는 경우 같은 메모리 레이아웃에 멤버를 모아둘 때 사용한다. 특히 공용체는 임베디드 시스템이나 커널모드 디바이스 드라이버 등에서 주로 사용하며 보통은 거의 쓰지않는다.
공용체와 엔디언
#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;
}
# 실행 결과
0x12345678
0x5678
0x78
4
printf로 출력해보면
d1.num2
는 저장한 숫자가 그대로 나오지만 다른 멤버는 숫자의 일부분만 나온다.공용체는 값을 저장하는 공간은 공유하지만 값을 가져올 때는 해당 자료형의 크기만큼 가져오기 때문이다.
d1.num
은 2바이트 크기의short
이므로 앞의 2바이트인0x5678
만 나온다. 마찬가지로d1.c1
은 1바이트 크기의char
이므로 1바이트인0x78
만 나온다.- 그런데 앞의 값만 나와야 한다면
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 union _Box{ // 공용체 이름은 _Box
short candy;
float snack;
char doll[8];
} Box; // typedef을 사용하여 공용체 별칭을 Box로 정의
-----------------------------------------------------------------------------------------------
typedef union { // 익명 공용체 정의
short candy;
float snack;
char doll[8];
} Box; // typedef을 사용하여 공용체 별칭을 Box로 정이ㅡ
Box b1; // 공용체 별칭으로 공용체 변수 선언
------------------------------------------------------------------------------------------------
union Box { // 공용체 정의
short cnady;
float snack;
char doll[8];
} b1; // 공용체를 정의하는 동시에 변수 b1 선언
공용체 포인터를 선언하고 메모리 할당하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h> // malloc, free
#include <string.h> // strcpy
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"); // 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;
}
# 실행 결과
8
25954
4464428256607938511036928229376.000000
bear
- 구조체와 마찬가지로 공용체 포인터도 멤버에 접근할 때는
->(화살표 연산자)
를 사용한다. typedef
로 정의한 공용체 별칭으로도 포인터를 선언하고 메모리를 할당할 수 있다.
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 ] 17. 연결리스트 구조체 ( Linked List ) (0) | 2020.11.30 |
---|---|
[ C ] 16. volatile 이용하기 (0) | 2020.08.27 |
[ C ] 14. goto에 관하여 (0) | 2020.08.14 |
[ C ] 13. 함수 포인터 배열 활용하기 (0) | 2020.08.14 |
[ C ] 12. 함수와 가변인자 (0) | 2020.08.14 |