30. 공용체(union) - 멤버간의 메모리 공유
구조체(struct)는 연관관계에 있는 여러가지 데이터를 그룹화하여 편리하게 사용하는 자료 구조입니다. 이와 비슷하게 사용자 정의 타입으로 union이 있습니다. 구조체와 선언하는 방식은 같고 struct 대신 union으로 이름만 변경된 자료형입니다.
union 공용체명
{
데이터type1 멤버명1;
데이터type2 멤버명2;
...
};
위와 같이 struct와 비슷한 형태롤 가집니다. struct와 union의 차이를 예를들어서 진행해보겠습니다.
예제). 구조체(struct)와 공용체(union)간의 구조 비교
#include <stdio.h>
struct ssample
{
char m1;
short m2;
int m3;
long m4;
};
union usample
{
char m1;
short m2;
int m3;
long m4;
};
int main(void)
{
struct ssample s;
union usample u;
printf("구조체(struct)\n");
printf("sizeof(struct ssample) = %d\n", sizeof(struct ssample));
printf("&s = %p\n&s.m1 = %p\n&s.m2 = %p\n&s.m3 = %p\n&s.m4 = %p\n",
&s, &s.m1, &s.m2, &s.m3, &s.m4);
printf("----------------------------\n");
printf("공용체(union)\n");
printf("sizeof(union usample) = %d\n", sizeof(union usample));
printf("&u = %p\n&u.m1 = %p\n&u.m2 = %p\n&u.m3 = %p\n&u.m4 = %p\n",
&u, &u.m1, &u.m2, &u.m3, &u.m4);
return 0;
}
실행 결과는
구조체(struct)
sizeof(struct ssample) = 16
&s = 0x7ffc00ef0570
&s.m1 = 0x7ffc00ef0570
&s.m2 = 0x7ffc00ef0572
&s.m3 = 0x7ffc00ef0574
&s.m4 = 0x7ffc00ef0578
----------------------------
공용체(union)
sizeof(union usample) = 8
&u = 0x7ffc00ef0568
&u.m1 = 0x7ffc00ef0568
&u.m2 = 0x7ffc00ef0568
&u.m3 = 0x7ffc00ef0568
&u.m4 = 0x7ffc00ef0568
위와 같습니다.
실행 결과의 특징을 보면 구조체의 각각의 멤버들은 모두 자신의 메모리의 크기와 위치를 각각 가지고 있음을 알 수 있습니다. 그러나 union은 모든 멤버들의 시작 위치가 같다는 것을 알 수 있습니다. 그리고 전체 크기를 보면, 구조체는 멤버 변수의 크기와 메모리상에 배수의 위치(29. 구조체(struct) - 구조체의 크기와 멤버 변수의 메모리 위치 참조) 등을 고려하여 크기가 정해진 반면, 공용체는 가장 큰 데이터 타입의 크기를 갖는 것을 알 수 있습니다.
이와 같이 공용체(union)은 모든 멤버변수들이 메모리를 공유하고, 가장 큰 데이터 타입의 크기를 가지게 됩니다. 즉, 멤버 변수에 값을 변경하면 메모리를 공유하므로 모든 멤버들의 데이터가 변경이 일어나게 됩니다.
예제). 멤버간의 데이터의 변화
#include <stdio.h>
union sample
{
unsigned int lvalue;
unsigned short svalue[2];
unsigned char cvalue[4];
};
int main(void)
{
union sample u;
u.lvalue = 0xA0B0C0D0;
printf("lvalue = 0x%X\n", u.lvalue);
for(int i = 0; i < 2; i++) {
printf("svalue[%d] = 0x%X\n", i, u.svalue[i]);
}
for(int i = 0; i < 4; i++) {
printf("cvalue[%d] = 0x%X\n", i, u.cvalue[i]);
}
return 0;
}
위의 프로그램은 각각의 변수들을 4바이트 형태의 멤버변수들로 구성했을 때, 그 값을 변경하면 어떤 영향을 주는 지에 대해서 알아보기 위한 샘플 코드입니다.
위의 프로그램을 실행 결과는
lvalue = 0xA0B0C0D0
svalue[0] = 0xC0D0
svalue[1] = 0xA0B0
cvalue[0] = 0xD0
cvalue[1] = 0xC0
cvalue[2] = 0xB0
cvalue[3] = 0xA0
위와 같이 멤버들은 메모리를 서로 공유하기 때문에 위와 같이 lvalue에 값을 대입하면 다른 멤버들도 값이 변하는 것을 확인하였습니다. lvalue에 0xA0B0C0D0를 대입하였는 데, 값들은 역순으로 저장된 것을 확인할 수 있습니다. 이는 테스트를 진행한 컴퓨터가 Intel계열의 CPU로서 little endian을 따르기 때문입니다. 만약 UNIX계열의 big endian 시스템에서 실행을 했다면 모두 0xA0B0C0D0 → 0xA0B0, 0xC0D0 → 0xA0, 0xB0, 0xC0, 0xD0 형식으로 출력되었을 것입니다.
초창기 16bit PC (8086: XT, 80286: AT)인 DOS시절에는 H/W를 제어하기 위해서는 프로그램에서 BIOS(Basic Input Output System)를 직접 access하여야 했습니다. 이 BIOS를 직접 제어를 하기 위해서는 CPU의 Register 메모리의 상위 바이트, 하위 바이트에 각각의 값을 달리 설정하는 등의 작업을 위하여 union을 많이 사용하였습니다.
union REGISTER
{
unsigned short f; // AX register의 전체 크기
struct {
unsigned short l : 8; // register의 하위 바이트
unsigned short h : 8; // register의 상위 바이트
};
};
위와 같이 하위 바이트는 l에, 상위 바이트는 h에 값을 대입하면, 합쳐진 값이 f에 저장됩니다.
예를들면, (오래되서 기억이 안남 int86함수의 사용법이 맞는 지 모르겠네요.)
union REGISTER r;
r.h = 20;
r.l = 10;
int86(0x16, r.f);
위와 같이 상위 바이트와 하위 바이트의 값을 각각 대입한 후에 합쳐진 값을 사용하거나 반대로 사용하게됩니다. 그렇지 않으면 만약 상위 바이트만 변경하고 싶다면,
f = (f & 0x00FF) | (20 << 8);
처럼 복잡한 bit 연산을 해야 할 것입니다.
이상과 같이 공용체(union)에 대해서 알아보았습니다.
공용체(union)는 멤버들간에 메모리 영역을 공유함으로서 서로간에 간섭이 발생하고 이를 활용하는 자료구조라고 이해하면 될 것 같습니다.
C Programming Language 문법
1. C 프로그래밍 언어는? 2. C언어 개발 환경 (실습 환경) 3. C언어의 컴파일 과정 4. C 소스 파일 구성 5. 주석문(Comment) 6. 식별자 명명 규칙 7. C 프로그래밍의 시작 - 함수 8. 변수와 상수 (정수형) 9. 변..
www.it-note.kr