C에서 비트 필드 설명: 작동 원리와 중요성

발행: (2026년 1월 10일 오후 01:14 GMT+9)
7 min read
원문: Dev.to

Source: Dev.to

번역할 텍스트를 제공해 주시면 한국어로 번역해 드리겠습니다.

Source:

비트 필드란 무엇인가?

비트 필드는 변수에 차지해야 할 비트 수를 정확히 지정할 수 있게 해 주는 특수한 구조체 멤버이며, 표준 바이트 정렬 크기를 사용하는 대신에 사용할 수 있습니다.

struct Date {
    unsigned int day   : 5;   // 5 bits  (Range: 0‑31)
    unsigned int month : 4;   // 4 bits  (Range: 0‑15)
    unsigned int year  : 11;  // 11 bits (Range: 0‑2047)
};

각 멤버마다 전체 int(보통 32비트)를 할당하는 대신, 컴파일러가 이러한 필드를 함께 압축하여 메모리 사용량을 줄일 수 있습니다.

구문 및 기본 규칙

  • 비트 필드는 구조체 멤버 이름 뒤에 콜론(:)을 붙이고, 그 뒤에 사용할 비트 수를 적어 정의합니다.
  • 비트 필드는 오직 struct 내부에서만 선언할 수 있으며, 독립적인 변수로 존재할 수 없습니다.
  • 비트 필드는 C에서 주소를 가질 수 없는 객체이므로 주소 연산자(&)를 적용할 수 없습니다.

비트 필드 vs 일반 구조체 멤버

일반 구조체 멤버는 바이트 경계에 맞춰 정렬되므로, int 하나가 작은 값만 저장하더라도 보통 4바이트를 차지합니다.

struct Flags {
    unsigned int a : 1;
    unsigned int b : 1;
    unsigned int c : 1;
};

반면 비트‑필드 멤버는 머신 워드 내의 인접한 비트에 압축될 수 있어, 여러 작은 값을 동일한 저장 공간에 공유할 수 있습니다. 트레이드‑오프는 다음과 같습니다:

측면일반 멤버비트 필드
레이아웃 예측 가능성고정, 바이트‑정렬구현‑정의(컴파일러가 결정)
메모리 사용량공간 낭비 가능(전체 바이트)컴팩트(지정된 비트만 사용)
주소 지정 가능성주소 지정 가능(&member)주소 지정 불가

컴파일러가 실제로 비트 필드를 저장하는 방법

C 표준은 비트 필드에 대해 특정 레이아웃을 보장하지 않습니다. 컴파일러가 결정합니다:

  • 저장 단위 내 비트 순서 (LSB vs. MSB).
  • 바이트 간 패킹 (필드가 바이트 경계를 넘을 수 있는지 여부).
  • 정렬 및 패딩 규칙.

그 결과, 동일한 아키텍처를 대상으로 하는 두 개의 서로 다른 컴파일러가 동일한 비트‑필드 구조에 대해 서로 다른 메모리 레이아웃을 생성할 수 있습니다. 이는 하드웨어 레지스터나 네트워크 프로토콜과 같이 정확하고 이식 가능한 비트‑레벨 레이아웃이 필요할 때 비트 필드가 부적합하게 만듭니다.

비트 필드의 적절한 사용

비트 필드는 프로그램 내부에서 논리적 상태를 표현할 때 가장 적합합니다:

  • 관련 플래그를 자연스럽게 그룹화합니다.
  • 수동 마스킹에 비해 가독성을 높입니다.
  • 정확한 메모리 레이아웃이 중요하지 않은 내부 플래그, 상태 머신, 설정 구조에 이상적입니다.

수동 비트 마스킹을 선호해야 할 경우

정확한 비트 위치가 중요한 경우에는 명시적인 비트 마스크를 사용합니다. 예를 들어:

  • 메모리‑맵드 하드웨어 레지스터.
  • 데이터시트에 정의된 바이너리 프로토콜.
  • 외부 사양과 일치해야 하는 모든 레이아웃.
/* Example: hardware UART register bits */
#define UART_RXNE (1 << 5)   // Receive not empty
#define UART_TC   (1 << 6)   // Transmission complete
#define UART_TXE  (1 << 7)   // Transmit empty

수동 마스킹은 덜 우아해 보일 수 있지만, 정확하고 이식 가능하며 모호함이 없다는 점에서 임베디드 시스템에 필수적인 특성을 제공합니다.

Rules of Thumb

  • 절대 비트 필드를 메모리‑매핑된 하드웨어 레지스터나 고정 레이아웃이 요구되는 프로토콜 헤더에 사용하지 마세요.
  • 사용 비트 필드를 내부 플래그 및 논리적 프로그램 상태에 적용하세요. 가독성이 정확한 배치보다 더 중요할 때.
  • 선호 비트 마스크( #define 또는 enum)를 하드웨어 레지스터, 바이너리 프로토콜, 혹은 비트 위치가 외부 문서에 정의된 모든 상황에서 사용하세요.
  • bool 멤버는 최소 한 바이트를 차지하고 패딩을 유발할 수 있다는 점을 기억하세요; 비트 필드는 같은 의미를 더 컴팩트하게 표현할 수 있습니다.
Back to Blog

관련 글

더 보기 »