비트 필드(Bit-field)
비트 필드(Bit-field)
구조체나 공용체 안에서 어떤 멤버가 차지하는 크기를 바이트가 아니라 비트 단위로 지정하고 싶을 때가 있어요. 그럴 때 쓰는 것이 바로 **비트 필드(bit-field)**예요. 비트 필드는 명시된 폭(비트 단위)을 가진 멤버 선언이고, 서로 인접한 비트 필드 멤버들은 묶여서(packed) 개별 바이트를 공유하거나 걸쳐서(straddle) 차지할 수도 있어요.
비트 필드 선언은 다음 선언자(declarator)를 사용하는 struct/union 멤버 선언이에요.
identifier(optional) : width |
| 자리 | 의미 |
|---|---|
identifier |
선언되는 비트 필드의 이름. 이름은 생략할 수 있어요. 이름 없는 비트 필드는 지정된 개수의 패딩 비트를 도입해요. |
width |
값이 0 이상, 기반 타입의 비트 수 이하인 정수 상수 표현식. 0보다 크면 이 비트 필드가 차지할 비트 수예요. 값 0은 이름 없는 비트 필드에만 허용되며 특별한 의미가 있어요. 다음 비트 필드가 할당 단위(allocation unit)의 경계에서 시작하도록 지정하는 거예요. |
출처: cppreference
본문
동작 방식(Explanation)
비트 필드는 다음 타입 중 하나만 가질 수 있어요(각각 const나 volatile로 한정될 수 있어요).
unsigned int— 부호 없는 비트 필드용. 예:unsigned int b : 3;은 범위가[0, 7]이에요.signed int— 부호 있는 비트 필드용. 예:signed int b : 3;은 범위가[-4, 3]이에요.int— 부호가 구현 정의인 비트 필드용. (다른 곳에서int가 "signed int"를 뜻하는 것과 다르다는 점을 주의하세요.) 예를 들어int b : 3;은 값의 범위가[0, 7]이거나[-4, 3]일 수 있어요._Bool— 1비트 비트 필드용. 예:bool x : 1;은 범위가[0, 1]이고, 이 타입으로/부터의 암시적 변환은 불리언 변환 규칙을 따라요. (C99부터)- 비트 정밀 정수 타입(bit-precise integer types) — 예:
_BitInt(5) : 4;는 범위가[-8, 7],unsigned _BitInt(5) : 4;는 범위가[0, 15]예요. (C23부터)
추가로 구현 정의 타입도 허용될 수 있어요. 비트 필드가 원자(atomic) 타입을 가질 수 있는지도 구현 정의예요. (C11부터)
비트 필드의 비트 수(width)는 그 필드가 담을 수 있는 값의 범위를 제한해요.
#include <stdio.h>
struct S
{
// three-bit unsigned field,
// allowed values are 0...7
unsigned int b : 3;
};
int main(void)
{
struct S s = {7};
++s.b; // unsigned overflow
printf("%d\n", s.b); // output: 0
}
여러 인접한 비트 필드는 묶여서(packed) 배치될 수 있고, 보통 그렇게 돼요.
#include <stdio.h>
struct S
{
// will usually occupy 4 bytes:
// 5 bits: value of b1
// 11 bits: unused
// 6 bits: value of b2
// 2 bits: value of b3
// 8 bits: unused
unsigned b1 : 5, : 11, b2 : 6, b3 : 2;
};
int main(void)
{
printf("%zu\n", sizeof(struct S)); // usually prints 4
}
특별한 경우로, 폭 0의 이름 없는 비트 필드는 패딩을 끊는 역할을 해요. 다음 비트 필드가 다음 할당 단위의 처음에서 시작하도록 지정하는 거예요.
#include <stdio.h>
struct S
{
// will usually occupy 8 bytes:
// 5 bits: value of b1
// 27 bits: unused
// 6 bits: value of b2
// 15 bits: value of b3
// 11 bits: unused
unsigned b1 : 5;
unsigned : 0; // starts a new unsigned int
unsigned b2 : 6;
unsigned b3 : 15;
};
int main(void)
{
printf("%zu\n", sizeof(struct S)); // usually prints 8
}
비트 필드는 반드시 바이트의 처음에서 시작하지 않기 때문에 비트 필드의 주소를 얻을 수 없어요. 비트 필드에 대한 포인터도 만들 수 없고요. 비트 필드는 sizeof나 _Alignas(C11부터)(C23 이전)/alignas(C23부터)와 함께 쓸 수 없어요. (C11부터)
주의할 점(Notes)
비트 필드의 다음 사용법은 **미정의 동작(undefined behavior)**을 일으켜요.
- 비트 필드에
offsetof를 호출하는 것.
비트 필드의 다음 속성은 **불특정(unpecified)**이에요.
- 비트 필드가 담긴 할당 단위의 정렬(alignment).
비트 필드의 다음 속성은 **구현 정의(implementation-defined)**예요.
int타입의 비트 필드를 부호 있는 것으로 취급할지 부호 없는 것으로 취급할지.int,signed int,unsigned int,_Bool(C99부터), (그리고unsigned일 수도 있는)_BitInt(N)(C23부터) 이외의 타입이 허용되는지 여부.- 원자 타입이 허용되는지 여부. (C11부터)
- 비트 필드가 할당 단위 경계를 걸쳐(straddle) 차지할 수 있는지 여부.
- 할당 단위 안에서 비트 필드의 순서(어떤 플랫폼에서는 비트 필드가 왼쪽에서 오른쪽으로, 또 어떤 곳에서는 오른쪽에서 왼쪽으로 묶여요).
_Bool의 객체 표현에서 비트 수가 적어도 CHAR_BIT인데도, _Bool 타입 비트 필드의 폭은 1보다 클 수 없어요. (C99부터)
C++ 프로그래밍 언어에서는 비트 필드의 폭이 기반 타입의 폭을 넘을 수 있는데(그러면 남는 비트는 패딩 비트예요), int 타입 비트 필드는 항상 부호 있는 것으로 취급돼요.
더 알아보기
struct/union: 비트 필드가 선언되는 문맥.- 메모리 정렬(alignment): 객체가 메모리에 배치되는 규칙.