54.2 공용체와 엔디언

공용체 멤버에 값을 저장하고 가져오는 방법을 좀 더 자세히 알아보겠습니다. 다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.

union_endian.c

#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

공용체 변수 d1의 멤버 중에서 가장 큰 자료형인 num20x12345678을 할당했습니다.

data.num2 = 0x12345678;

printfd1.num2, d1.num1, d1.c1을 출력해보면 d1.num2는 저장한 숫자가 그대로 나오지만 다른 멤버는 숫자의 일부분만 나옵니다. 공용체는 값을 저장하는 공간은 공유하지만 값을 가져올 때는 해당 자료형의 크기만큼 가져오기 때문이죠.

0x12345678
0x5678
0x78

d1.num1은 2바이트 크기의 short이므로 앞의 2바이트인 0x5678만 나옵니다. 마찬가지로 d1.c1은 1바이트 크기의 char이므로 앞의 1바이트인 0x78만 나오게 됩니다. 그런데 앞의 값만 나와야 한다면 0x1234와 0x12가 나와야 하는데 왜 0x5678, 0x78이 나오는 걸까요?

우리가 사용하는 x86(x86-64) 계열 CPU는 리틀 엔디언이라는 방법으로 값을 메모리에 저장합니다. 간단하게 이야기하면 리틀 엔디언은 숫자를 1바이트씩 쪼개서 낮은 자릿수가 앞에 옵니다. 사람이 보기에는 반대로 뒤집혀 있죠.

그림 54‑4 리틀 엔디언과 빅 엔디언

0x12345678을 리틀 엔디언 방식으로 메모리에 저장하면 78 56 34 12가 됩니다. 공용체는 앞에서부터 자료형의 크기만큼 값을 가져오게 되므로 d1.num1은 앞의 2바이트 56 78을 가져오게 되고, d1.c1은 앞의 1바이트 78만 가져오게 되는 것입니다(저장할 때 뒤집혀서 저장되었으므로 가져올 때는 다시 되돌려서 가져옵니다. 따라서 78 56이 아니라 56 78이 됩니다).

그림 54‑5 리틀 엔디언과 공용체

참고 | typedef와 익명 공용체

공용체도 구조체와 마찬가지로 typedef로 별칭을 지정하고, 익명 공용체를 정의할 수 있습니다.

먼저 공용체 별칭은 다음과 같이 정의합니다.

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

typedef_union.c

typedef union _Box {    // 공용체 이름은 _Box
    short candy;
    float snack;
    char doll[8];
} Box;                  // typedef를 사용하여 공용체 별칭을 Box로 정의

typedef로 공용체의 별칭을 만들었다면 변수는 다음과 같이 선언합니다.

  • 공용체별칭 변수이름;

익명 공용체는 다음과 같이 정의합니다.

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

변수는 공용체 별칭으로 선언하면 됩니다.

  • 공용체별칭 변수이름;

typedef_anonymous_union.c

typedef union {    // 익명 공용체 정의
    short candy;
    float snack;
    char doll[8];
} Box;             // typedef를 사용하여 공용체 별칭을 Box로 정의

Box b1;    // 공용체 별칭으로 공용체 변수 선언
참고 | 공용체를 정의하는 동시에 변수 선언하기

다음과 같이 공용체는 정의하는 동시에 변수를 선언할 수 있습니다.

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

예)

union_variable.c

union Box {    // 공용체 정의
    short candy;
    float snack;
    char doll[8];
} b1;          // 공용체를 정의하는 동시에 변수 b1 선언