C 언어 공용체 활용: 효율적인 데이터 패킹과 언패킹
C 언어 공용체를 이용한 효율적인 데이터 패킹과 언패킹
공용체는 동일한 바이트를 다른 데이터 타입으로 재해석하는 강력하고 메모리 효율적인 방법을 제공합니다. 원래는 상호 배타적인 변수 간의 공유 메모리를 위해 의도되었지만, 최신 C 프로그래머들은 직렬 프로토콜이나 저수준 통신 처리 시 데이터 패킹, 언패킹, 타입 펀닝을 위해 공용체를 자주 사용합니다.
공용체의 메모리 공유 방식
공용체의 모든 멤버는 동일한 메모리 영역을 공유합니다. 가장 큰 멤버가 공용체의 크기를 결정하며, 모든 멤버는 같은 주소에서 시작합니다.
union { uint16_t word; struct { uint8_t byte1; uint8_t byte2; }; } u1;
여기서 word는 2바이트를 차지하고, 중첩 구조체는 이 2바이트를 byte1과 byte2로 분할합니다. 레이아웃은 다음과 같이 시각화할 수 있습니다:

두 바이트를 16비트 워드로 패킹
두 8비트 값 x와 y를 16비트 워드로 결합하려면 구조체 멤버에 할당한 후 word 멤버를 읽습니다:
u1.byte1 = y; u1.byte2 = x; uint16_t packed = u1.word;
이 '데이터 펀닝'은 간단하지만 프로세서의 바이트 순서에 의존합니다.
프로세서 엔디안이 중요하다
리틀 엔디안 시스템은 최하위 바이트를 먼저 저장하고, 빅 엔디안 시스템은 최상위 바이트를 먼저 저장합니다. 시퀀스 0x01020304를 고려해 보세요:

리틀 엔디안 머신에서 간단한 테스트를 실행하면:
#include <stdio.h> #include <stdint.h> int main() { union { struct { uint8_t byte1; uint8_t byte2; }; uint16_t word; } u1; u1.byte1 = 0x21; u1.byte2 = 0x43; printf("Word is: %#X\n", u1.word); return 0; }
출력: Word is: 0X4321 — 리틀 엔디안 저장임을 확인합니다. 빅 엔디안 타겟에서 같은 코드는 0X2143을 출력합니다.
비트 연산 대안: 엔디안 독립적 패킹
시프트와 마스킹은 16비트 값을 구축하는 이식 가능한 방법을 제공합니다:
uint16_t word2 = (((uint16_t)byte3) << 8) | ((uint16_t)byte4);
두 방법 비교:
#include <stdio.h> #include <stdint.h> int main() { union { struct { uint8_t byte1; uint8_t byte2; }; uint16_t word1; } u1; u1.byte1 = 0x21; u1.byte2 = 0x43; printf("Word1 is: %#X\n", u1.word1); uint8_t byte3 = 0x21, byte4 = 0x43; uint16_t word2 = (((uint16_t)byte3) << 8) | ((uint16_t)byte4); printf("Word2 is: %#X\n", word2); return 0; }
빅 엔디안 프로세서: Word1 is: 0X2143, Word2 is: 0X2143. 리틀 엔디안 프로세서: Word1 is: 0X4321, Word2 is: 0X2143. 비트 연산 접근 방식은 아키텍처 간 일관된 결과를 보장합니다.
실전 예제: UART로 float 전송
직렬 링크는 일반적으로 한 번에 한 바이트씩 전송합니다. 32비트 float를 전송하기 위해 공용체를 사용하여 float를 4바이트 배열로 볼 수 있습니다:
union buffer { float f; struct { uint8_t byte[4]; }; };
송신 측:
union buffer tx; union buffer rx; float f1 = 5.5f; tx.f = f1; for (int i = 0; i < 4; ++i) { // UART로 tx.byte[i] 전송 rx.byte[i] = tx.byte[i]; // 시뮬레이션 수신 } printf("The received data is: %f\n", rx.f);
출력: The received data is: 5.500000. 공용체는 바이트 표현과 부동소수점 값을 깔끔하게 분리하여 코드를 읽기 쉽고 유지보수하기 쉽게 만듭니다.

핵심 요약
- 공용체는 타입 펀닝을 허용하지만 결과는 엔디안에 의존합니다.
- 비트 연산자는 이식 가능하고 아키텍처 독립적인 패킹을 제공합니다.
- 명확성과 유지보수를 위해 많은 개발자가 임베디드 컨텍스트에서 여전히 공용체를 선호합니다.
- 공용체는 바이트 지향 링크를 통해 임의의 데이터 구조를 전송할 때 빛을 발합니다.
C, 공용체, 임베디드 시스템에 대해 더 알아보려면 전체 기사 목록을 둘러보세요.