C의 union

union은 struct와 문법이 거의 비슷하다.

typedef struct s_my_struct
{
    int     foo;
    float   bar;
}   t_my_struct;

typedef union u_my_union
{
    int     foo;
    float   bar;
}   t_my_union;

struct는 모든 멤버를 포함하는 데 반해 union은 멤버 중 하나만 포함한다.

위 예시에서 struct는 foo와 bar를 각각 저장할 수 있는데, union은 둘 중 하나만 저장할 수 있다.

그 이유는 union의 모든 멤버는 같은 메모리 공간을 공유하기 때문이다.

struct는 모든 멤버 크기를 합한 만큼 공간을 차지하는 데 반해, union은 제일 큰 멤버만큼만 차지한다.

Union의 사용처

union만 갖고 할 수 있는 것은 그다지 많지 않아 보인다.

t_my_union 타입의 변수가 있어도, 이게 foo를 담고 있는지 bar를 담고 있는지 모르기 때문이다.

같은 데이터에 대한 다른 표현

예를 들면 같은 색상 데이터를 색상 코드나 byte 배열, 또는 구조체로 나타낼 수 있겠다.

typedef struct s_s_color
{
    uint8_t alpha;
    uint8_t red;
    uint8_t green;
    uint8_t blue;
}   t_s_color;

typedef union u_color
{
    uint32_t    code;
    uint8_t     array[4];
    t_s_color   argb;
}   t_color;

하지만 이 예시에는 큰 결함이 있는데, 바로 엔디언이다.

0xFF440088에서 FF가 과연 alpha에 들어갈까? 리틀 엔디언에서는 blue에 들어갈 것이다.

reinterpret_cast

float   u32_to_float(uint32_t u)
{
    union s_u32_or_float
    {
        uint32_t    u;
        float       f;
    }   a;

    a.u = u;
    return (a.f);
}

uint32_tfloat이 모두 32비트라는 것을 알고 있으므로 uint32_t를 받아 그와 같은 비트열로 이루어진 float으로 바꾸거나, 그 역이 가능하다.

하지만 이는 union을 사용하지 않고도 가능하다.

float   float_to_u32(float f)
{
    return (*((uint32_t *)&f));
}

선택적 변수

void    print(t_my_union u, bool is_foo)
{
    if (is_foo)
        printf("%d\\n", u.foo);
    else
        printf("%f\\n", u.bar);
}

어떤 조건에 따라 어떤 멤버로 사용해야 할지 결정될 때가 있다. 사실 거의 항상 그렇다.

그런 경우 union과 함께 그 union을 어떻게 해석해야 할지, 그 타입 정보를 같이 전달할 수 있다.

하지만 이렇게 쓸 거라면 union과 타입 정보를 같이 전달하는 것이 낫지 않을까?

Tagged Union

그래서 등장한 것이 tagged union이다.

union과 그 union을 어떤 멤버로 해석해야 할지를 저장하는 enum을 한데 묶은 것이다.

typedef enum e_my_union_type
{
    MY_UNION_TYPE_A,
    MY_UNION_TYPE_B,
    MY_UNION_TYPE_C,
}   t_my_union_type;

// t_my_type_a, t_my_type_b, t_my_type_c

typedef union u_my_union_value
{
    t_my_type_a a;
    t_my_type_b b;
    t_my_type_c c;
}   t_my_union_value;

typedef struct s_my_union
{
    t_my_union_type     type;
    t_my_union_value    value;
}   t_my_union;
typedef enum e_my_union_type
{
    MY_UNION_TYPE_A,
    MY_UNION_TYPE_B,
    MY_UNION_TYPE_C,
}   t_my_union_type;

typedef struct s_my_union_a
{
    t_my_union_type type;
    // ... a
}   a;

typedef struct s_my_union_b
{
    t_my_union_type type;
    // ... b
}   b;

typedef struct s_my_union_c
{
    t_my_union_type type;
    // ... c
}   c;

typedef union u_my_union
{
    t_my_union_type type;
    t_my_union_a    a;
    t_my_union_b    b;
    t_my_union_c    c;
}   t_my_union;

위 예시를 포함해 여러가지 유형으로 나타날 수 있는데, 이들 모두 tagged union이다.