C 비트필드로 메모리 최적화: 플래그 압축과 하드웨어 매핑
비트필드(Bit-field)는 구조체 멤버의 비트 폭을 명시하여 메모리를 비트 단위로 압축하는 기능입니다. 플래그 집합, 하드웨어 레지스터 매핑, 프로토콜 헤더 파싱에 유용합니다.
비트필드 문법
struct {
타입 멤버명 : 폭;
};
타입: int, signed int, unsigned int, _Bool (C99)
폭: 비트 수 (타입 폭 이하)
이름 없는 비트필드: unsigned int : 3; → 패딩용
폭 0: unsigned int : 0; → 다음 정렬 경계로 강제 이동
기본 예제: 플래그 압축
// 8개 불리언 플래그 → 1바이트로 압축
struct Flags {
unsigned int read : 1;
unsigned int write : 1;
unsigned int execute : 1;
unsigned int hidden : 1;
unsigned int system : 1;
unsigned int archive : 1;
unsigned int : 2; // 예약/패딩 2비트
};
struct Flags f = {0};
f.read = 1;
f.write = 1;
if (f.read && f.write) { ... }
// 크기 확인
printf("%zu\n", sizeof(struct Flags)); // 보통 4바이트 (int 크기로 정렬)
비트필드 메모리 레이아웃 (구현 의존적)
레이아웃 예시 (리틀엔디안, 32비트 int 기준)
struct {
unsigned int a : 4; // 비트 0-3
unsigned int b : 4; // 비트 4-7
unsigned int c : 8; // 비트 8-15
unsigned int d : 16; // 비트 16-31
} packed; // 크기 4바이트, 패딩 없음
struct {
unsigned int a : 4;
unsigned int : 0; // 32비트 경계로 강제 이동
unsigned int b : 4; // 다음 워드 비트 0-3
} aligned; // 크기 8바이트
실전 활용 예제
1. 파일 권한 비트 (유닉스 style)
struct FilePerm {
unsigned int other_exec : 1;
unsigned int other_write : 1;
unsigned int other_read : 1;
unsigned int group_exec : 1;
unsigned int group_write : 1;
unsigned int group_read : 1;
unsigned int user_exec : 1;
unsigned int user_write : 1;
unsigned int user_read : 1;
unsigned int sticky : 1;
unsigned int setgid : 1;
unsigned int setuid : 1;
unsigned int : 4; // 패딩
};
2. 네트워크 프로토콜 헤더 (TCP 플래그)
struct TCPFlags {
unsigned int fin : 1;
unsigned int syn : 1;
unsigned int rst : 1;
unsigned int psh : 1;
unsigned int ack : 1;
unsigned int urg : 1;
unsigned int ece : 1;
unsigned int cwr : 1;
}; // 1바이트에 8개 플래그
3. 하드웨어 레지스터 매핑
// GPIO 제어 레지스터 (가상 예시)
struct GPIO_Reg {
unsigned int pin0 : 1;
unsigned int pin1 : 1;
unsigned int pin2 : 1;
unsigned int pin3 : 1;
unsigned int pin4 : 1;
unsigned int pin5 : 1;
unsigned int pin6 : 1;
unsigned int pin7 : 1;
unsigned int mode : 4; // 0=입력, 1=출력, 2=대체기능...
unsigned int pull : 2; // 0=없음, 1=풀업, 2=풀다운
unsigned int : 6; // 예약
}; // 32비트 레지스터 1개에 매핑
4. 압축된 데이터 구조
// 날짜 저장: 년(12비트: 0~4095), 월(4비트: 1~12), 일(5비트: 1~31) = 21비트
struct CompactDate {
unsigned int year : 12;
unsigned int month : 4;
unsigned int day : 5;
}; // 4바이트 (32비트 정렬) vs 구조체 12바이트 절약
제약사항과 주의점
| 제약 | 설명 |
| 주소 연산 불가 | &bitfield 컴파일 에러 |
| 배열 불가 | 비트필드 배열 선언 불가 |
| 포인터 불가 | 비트필드 포인터 선언 불가 |
| 타입 제한 | int, unsigned int, _Bool만 표준 보장 (컴파일러 확장 가능) |
| 부호 있는 비트필드 | 음수 표현 가능하나 구현 의존적 (2의 보수 일반적) |
| 오버플로 동작 | 폭 초과 값 대입 시 구현 정의 (보통 잘림) |
| 스레드 안전성 | 인접 비트필드 동시 수정 시 경쟁 발생 (RMW 연산) |
비트필드 vs 비트 연산 수동 구현
// 비트필드 (가독성 좋음, 레이아웃 제어 제한)
struct { unsigned int flag : 1; } bf;
bf.flag = 1;
// 수동 비트 연산 (완전 제어, 이식성 높음)
#define FLAG_BIT 0
unsigned int flags = 0;
flags |= (1 << FLAG_BIT); // 설정
flags &= ~(1 << FLAG_BIT); // 해제
bool is_set = flags & (1 << FLAG_BIT); // 검사
모범 사례
플래그/상태 압축용으로만 사용, 일반 데이터엔 구조체 멤버 권장
unsigned int 기반으로 통일 (부호 있는 비트필드 회피)
명시적 패딩(: 0, : n)으로 레이아웃 제어
하드웨어/프로토콜 매핑 시 엔디언과 비트 순서 문서화
멀티스레드 환경에서 인접 비트필드 동시 접근 시 원자적 연산 또는 락 사용
이식성 중요 시 수동 비트 마스크/시프트 매크로/함수로 구현
C 언어
-
C# 비트 연산자와 시프트 연산자 완전 정복: 종합 가이드
-
C 사용자 정의 함수 4가지 검증된 패턴
-
C 언어에서 1차원 및 2차원 배열을 함수에 전달하는 방법 – 실전 가이드
-
C 구조체와 포인터 마스터하기: 실전 가이드
-
Java this 키워드 완벽 가이드: 용도, 생성자, 실전 예제
-
Java 캡슐화 마스터: 데이터 은닉 실전 가이드
-
C++ 구조체(struct) 완벽 가이드: 선언, 인스턴스, 포인터, 함수 인자 전달
-
C 함수 마스터: 선언, 정의, 호출, 인자 전달(값/참조), 재귀
-
C 언어 가변 인자 마스터하기: 실전 가이드
-
MATLAB 적분 마스터: 부정적분부터 리만 합, 정적분 응용까지