임베디드 C 공용체 완전 가이드: 효율적인 메모리 공유와 메시징
임베디드 C에서 공용체 이해하기
임베디드 시스템에서 공용체는 서로 다른 데이터 타입을 동일한 메모리 위치에 저장할 수 있게 하는 강력한 데이터 구조입니다. 이 글은 공용체의 기초를 다루고 실전 예제를 보여주며, 메모리 제약과 대역폭 민감한 애플리케이션에서 공용체가 필수 도구인 이유를 설명합니다.
임베디드 C에서 구조체와 공용체의 차이
이전에 구조체를 다뤘습니다: 이질적인 변수를 단일 엔티티로 그룹화하는 방법입니다. C에서 공용체는 다른 타입을 그룹화한다는 점에서 구조체와 보완적이지만, 핵심적인 차이점이 있습니다—모든 멤버가 동일한 메모리 주소를 공유합니다.
입문 예제
공용체 선언은 구조체와 거의 동일하며, 단순히 struct를 union으로 바꾸면 됩니다:
union test { uint8_t c; uint32_t i; };
템플릿은 두 멤버를 정의합니다: c는 1바이트, i는 4바이트를 차지합니다. 이 타입의 변수를 생성하려면:
union test u1;
멤버 접근에는 점 연산자를 사용합니다:
u1.i = 10; m = u1.c; // m은 uint8_t여야 함
공용체의 저장 크기는 가장 큰 멤버의 크기와 같으므로 u1은 4바이트를 차지합니다. i에 쓰면 c가 사용할 동일한 메모리를 덮어쓰므로 두 멤버는 상호 배타적입니다.
공유 메모리 공간이 필요한가?
상태, 속도, 위치 데이터를 교환하는 두 임베디드 장치 장치 A와 장치 B를 상상해 보세요(그림 1 참조). 데이터 항목과 크기는 다음과 같습니다:

그림 1
| 변수 |
크기(바이트) |
의미 |
| power |
1 |
배터리 충전량 |
| op_mode |
1 |
동작 모드 |
| temp |
1 |
주변 온도 |
| x_pos |
2 |
X 좌표 |
| y_pos |
2 |
Y 좌표 |
| vel |
2 |
속도 |
장치 B가 모든 정보가 필요한 경우, 단순 구조체라면 9바이트가 필요하고 각 통신 라운드는 9바이트 프레임을 전송합니다(그림 2). 그러나 많은 실제 시나리오에서 세 메시지 유형은 상호 배타적입니다: 때로는 상태만, 때로는 위치만, 때로는 속도만 필요합니다. 이 경우 가장 큰 페이로드는 4바이트(위치 메시지)입니다. 4바이트 버퍼를 공유하는 공용체는 세 메시지 중 어느 것이든 담을 수 있어 프레임 크기를 9에서 4바이트로 줄입니다(그림 3).

그림 2

그림 3
메시지 패킷에 공용체 사용
아래에서 상태용 구조체와 위치용 구조체를 만들고, 속도 필드와 함께 공용체에 임베디드합니다:
struct status_s { uint8_t power; uint8_t op_mode; uint8_t temp; }; struct position_s { uint16_t x_pos; uint16_t y_pos; }; union msg_union { struct status_s status; struct position_s position; uint16_t vel; };
공용체의 크기는 4바이트(position_s의 크기)며, 세 메시지 타입 간에 공유됩니다.
공용체 활성 멤버 추적 방법
수신자가 공용체를 읽을 때 어떤 멤버가 유효한지 알아야 합니다. 일반적인 패턴은 태그된 공용체입니다: 활성 멤버를 기록하는 별도 필드. 공용체 앞에 1바이트 msg_type을 추가할 수 있습니다:
struct message_s { uint8_t msg_type; // 's' = 상태, 'p' = 위치, 'v' = 속도 union { struct status_s status; struct position_s position; uint16_t vel; } msg; };
msg_type 바이트를 공용체와 함께 전송함으로써 수신자는 페이로드 형태에 대한 사전 지식 없이 올바르게 해석할 수 있습니다.
대안: 동적 메모리 할당
또 다른 접근법은 각 메시지 타입에 대해 즉석에서 메모리를 할당하는 것입니다. 송신자는 msg_type을 설정하고 해당 페이로드에 딱 맞는 바이트를 할당합니다. 수신자는 msg_type을 기반으로 수신 데이터를 해석하고 완료 시 메모리를 해제합니다. 이는 약간 더 메모리 효율적일 수 있지만, 동적 할당은 런타임 오버헤드와 명시적 해제의 필요성을 도입하므로 많은 임베디드 개발자가 정적 공용체 전략을 선호합니다.
다음: 공용체의 응용
공용체는 더 큰 워드에서 작은 데이터 조각을 추출하는 데도 자주 사용됩니다—통신 프로토콜이나 하드웨어 레지스터 파싱 시 특히 유용한 기술입니다. 이 시리즈의 다음 기사에서 이러한 사용 사례를 자세히 다룰 예정입니다.
임베디드 C에 대한 더 많은 기사를 보려면 기사 인덱스를 방문하세요.