반응형

여러 멤버를 공유하는 하나의 값을 공용체이라고 한다. 공용체는 구조체와 비슷하지만, 모든 구성원은 동일한 영역을 의미하며, 공용체의 인스턴스는 멤버 중 가장 큰 크기에 맞게 만들어 진다. 단일 값을 여러 형태로 표현하고 싶은 경우에 적용 할 수 있다.

다른 형태의 메모리를 공유

포인터 형변환을 잘 활용하여 어느 형을 다른 형처럼 사용할 수 있었다. 4개의 int 형의 멤버를 가지는 구조체의 인스턴스는 int 형 포인터로 캐스팅하여 4개의 요소를 가지는 int 형 배열로 처리할 수 있다. 이는 데이터가 메모리에 어떻게 기록되어 있는가하는 원리를 아는 중요한 실마리가 될 것이다.

이 생각을 발전시켜, 더 실제적으로 행동하는 것이 공용체이다. 공용체의 구문은 매우 구조체와 비슷하지만, 모든 멤버가 동일한 기록 영역을 공유한다는 점에서 그 성격이 크게 다르다. 예를 들어 수학의 행렬을 생각해 보자. 3차원 그래픽 프로그래밍에서는 자주 행렬 연산을 한다. float 형에서 4 × 4 행렬을 구현하자면 float matrix[4*4]으로도 float matrix[4][4]으로도 동일하며, 행렬의 각 요소를 모두 개별 멤버로 보유해도 상관없다. 어떤 형식이 좋은가하는 것은 대부분은 프로그래머의 취향의 문제가 될 것이다. 공용체는 이러한 문제를 한번에 해결해주는 효과적인 수단이다.

공용체는 구조체의 struct 대신에 union 키워드를 사용한다.

공용체 선언

union 태그명 {
형식 멤버명;
...
} 공용체 변수명;

태그명을 생략하고 익명의 공용체를 만들 수 있다는 점에서도 구조체와 동일한다. 공용체는 Pascal 언어 경험자에게는 가변 레코드와 유사한 기능이라고 설명하는 것이 알기 쉬울지도 모른다. 공용체의 모든 구성원이 동일한 주소를 돌려준다. 이것은 공용체가 동일한 저장 공간을 공유하고 있음을 증명하고 있다. 또한 공용체의 멤버에 액세스하는 방법은 구조체와 동일하다.

코드1

#include <stdio.h>

union Value {
    unsigned char chValue;
    int iValue;
};

int main() {
    union Value u;
    u.iValue = 0xFFFF;

    printf(
        "chValue = %08X : &chValue = %p\n"
        "iValue = %08X : &iValue = %p\n" ,
        u.chValue , &u.chValue , u.iValue , &u.iValue
    );
    return 0;
}

코드1은 공용체 Value를 선언하고 있다. 이 공용체는 unsigned char 형의 멤버 chValue과 int 형의 멤버 iValue을 보유하고 있지만, 구조체와는 다르게 이 멤버들은 저장 공간을 공유하고 있다. 따라서 실행 결과에서 확인할 수 있듯이 chValue 멤버의 값을 변경은 iValue 멤버에도 영향을 받고, iValue 멤버의 변경은 chValue 멤버에 영향을 받는다.

먼저 &chValue과 &iValue가 같은 주소를 반환하는 것을 유의한다. 이 결과에서 공용체가 동일한 저장 공간을 공유하고 있다는 것이 증명되고 있다. 공용체는 모든 멤버를 공유하기 위해서 가장 큰 멤버 형식에 따라 저장 공간을 확보한다. 코드1의 Value 구조체는 int 형에 맞춘다. chValue은 확보되어 있는 int 형의 기억 영역 중에 하위 1바이트를 공유하고 있는 것이다. 따라서 공용체 변수 u가 보유하고 있는 값은 0xFFFF이지만, chValue에 액세스한 경우 하위 1바이트 밖에 볼 수 없기 때문에 0xFF가 반환된다.

공용체는 구조체 마찬가지로 구조체나 공용체, 배열과 같은 복잡한 형태를 멤버로 보유할 수 있다. 예를 들어, 다음과 같은 복잡한 형태를 만들 수 있다.

union {
    struct {
        float _11, _12, _13, _14;
        float _21, _22, _23, _24;
        float _31, _32, _33, _34;
        float _41, _42, _43, _44;
    } matrix ;
    float m[4][4];
} u ;

이 공용체는 멤버에 16개의 float 형 멤버를 가지는 구조체와 4 × 4 개의 요소를 가지는 float 형 2차원 배열을 선언하고 있다. 구조체와 배열은 모두 16개의 float 형 요소를 보유한다는 점에서 이러한 멤버에 필요한 저장 공간의 크기는 동일하다. 개발자는 구조체의 각 멤버에 액세스할 수 있으며, 배열에서 인덱스를 지정해 액세스할 수 있다. 이러한 공용체는 3차원 그래픽의 좌표 변환을 위한 행렬에 사용된다. 구조체의 멤버로는 u.matrix._11와 같이 외부에서 순차적으로 멤버 지정한다.

공용체의 초기화

공용체의 초기화는 구조체 혹은 배열과 같이 각각의 요소에 수행할 수 없다. 왜냐하면 공용체의 멤버는 저장 공간을 공유하고 있기 때문에, 공용체가 보유하는 요소의 실체는 1개로 간주하기 때문이다. 그래서 공용체의 초기화는 첫번째 멤버 형에서만 초기화를 할 수 있다.

코드2

#include <stdio.h>

union Point {
    struct {
        short int x , y;
    } point;
    int location;
};

int main() {
    union Point u = { 100 , 50 };

    printf(
        "x = %d : y = %d\nlocation X = %d : location Y = %d\n" ,
        u.point.x , u.point.y , (short int)u.location , u.location >> 16
    );
    return 0;
}

이 프로그램의 Point 공용체는 32비트 컴퓨터 시스템에 있어서, 상위 16비트와 하위 16비트에 논리적으로 분할된 32비트에 팩(pack)된 좌표를 표시하는 사양에 적합하다. 이 공용체의 첫번째 멤버는 short int 형의 x와 y를 보유하는 무명 구조체이다. 따라서 초기화에는 short int 형의 두 정수를 지정할 수 있다. 이것은 개발자가 좌표를 지정하는 경우는 직관적인 사양이므로 환영받을 것이다.

그러나 시스템은 사정상 32비트 값으로 일괄하는 것이 더 빠르게 처리할 수 있을지도 모른다. 이 경우에는 location 멤버에 액세스하면 좋을 것이다. 이러한 공용체는 사용법에 따라 팔방 미인이 될 수 있다. 다만, 코드2는 int 형이 32비트, short int 형이 16비트인 것을 상정하고 있다. 그 이외의 환경에서 올바른 결과를 표시하지 않으므로 주의하자.

반응형

+ Recent posts