구조체(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

 

블로그 이미지

자연&사람

행복한 개발자 programmer since 1995.

Tag ,

댓글을 달아 주세요