C구조체와 문자열

union 기초

모든 멤버가 같은 메모리 주소를 공유하는 `union`의 메모리 구조, 같은 비트를 다른 타입으로 읽는 타입 펀닝 패턴, `enum`과 결합한 태그 공용체(tagged union)를 정리합니다.

마지막 수정 2026년 3월 26일

기본 패턴

c
#include <stdio.h>

union Value {
    int   i;
    float f;
    char  bytes[4];
};

union Value v;
v.i = 0x3F800000;
printf("float: %f\n", v.f);       // 1.000000 — 같은 비트를 float로 읽음
printf("byte0: %02X\n", (unsigned char)v.bytes[0]);  // 00

설명

모든 멤버는 같은 주소를 공유한다

struct는 각 멤버마다 별도의 메모리를 할당하지만, union모든 멤버가 같은 시작 주소를 공유합니다. union의 크기는 가장 큰 멤버의 크기입니다. 한 멤버에 값을 쓰면 다른 멤버로 읽을 때 같은 비트를 다른 타입으로 해석합니다.

c
union U {
    int    i;    // 4바이트
    double d;    // 8바이트
    char   c;    // 1바이트
};

printf("%zu\n", sizeof(union U));   // 8 — 가장 큰 멤버(double) 크기

union U u;
u.i = 42;
// u.d, u.c는 현재 정의되지 않은 값 — 마지막으로 쓴 멤버만 유효
printf("%d\n", u.i);   // 42 ✅

타입 펀닝 — 같은 비트를 다른 타입으로 해석

union을 이용하면 포인터 캐스트 없이 같은 비트 패턴을 다른 타입으로 읽을 수 있습니다. 네트워크 패킷 파싱이나 부동소수점 비트 조작에 활용됩니다.

c
// float의 비트 패턴을 int로 읽기 — C에서 합법적인 타입 펀닝
union FloatBits {
    float    f;
    uint32_t bits;
};

union FloatBits fb;
fb.f = 1.0f;
printf("bits: %08X\n", fb.bits);   // 3F800000 — IEEE 754 표현

// ❌ 포인터 캐스트는 UB (strict aliasing 위반)
// uint32_t bits = *(uint32_t *)&fb.f;  // 정의되지 않은 동작

// ✅ union 타입 펀닝은 C99/C11 표준에서 허용

태그 공용체 — 타입 안전한 variant

union만 쓰면 현재 어느 멤버가 유효한지 알 수 없습니다. enum으로 활성 멤버를 추적하는 태그 공용체(tagged union) 패턴이 이 문제를 해결합니다.

c
typedef enum { VAL_INT, VAL_FLOAT, VAL_STRING } ValType;

typedef struct {
    ValType type;   // 어떤 멤버가 유효한지 표시
    union {
        int         i;
        float       f;
        const char *s;
    } data;
} Value;

void print_value(const Value *v) {
    switch (v->type) {
        case VAL_INT:    printf("int: %d\n",    v->data.i); break;
        case VAL_FLOAT:  printf("float: %f\n",  v->data.f); break;
        case VAL_STRING: printf("str: %s\n",    v->data.s); break;
    }
}

Value a = { .type = VAL_INT,    .data.i = 42 };
Value b = { .type = VAL_STRING, .data.s = "hello" };
print_value(&a);   // int: 42
print_value(&b);   // str: hello

빠른 정리

상황적합한 선택
멤버들이 동시에 유효할 때struct
같은 메모리를 여러 타입으로 쓸 때union
여러 타입 중 하나를 안전하게 담을 때enum + union (태그 공용체)
float/int 비트 변환union 타입 펀닝 (*(T*)&x 대신)

주의할 점

마지막으로 쓴 멤버 외의 멤버를 읽으면 정의되지 않은 동작입니다 (타입 펀닝 예외 제외).

c
union U { int i; float f; };
union U u;
u.i = 1;

// ❌ i를 쓴 후 f를 읽는 것은 타입 펀닝 — C에서는 허용하지만
//    C++에서는 UB. 의도를 명확히 주석으로 남길 것
printf("%f\n", u.f);

// ❌ union 크기를 struct처럼 가정하면 안 됨
union Bad { char c; int i; };
printf("%zu\n", sizeof(union Bad));  // 4 (int 크기), char 1이 아님

// ✅ 태그 공용체로 항상 활성 멤버를 명시

참고 링크

1 sources