56 구조체 비트 필드 사용하기

지금까지 구조체의 멤버는 각 자료형 크기만큼 공간을 차지했습니다. 하지만 구조체 비트 필드를 사용하면 구조체 멤버를 비트 단위로 저장할 수 있습니다.

특히 CPU나 기타 칩의 플래그를 다루는 저수준(low level) 프로그래밍을 할 때 기본 자료형보다 더 작은 비트 단위로 값을 가져오거나 저장하는 경우가 많으므로 구조체 비트 필드가 유용하게 사용됩니다.

참고로 비트 필드는 내용이 다소 어려운데다 접할 기회가 많지 않으므로 완벽하게 이해하지 않아도 됩니다.

56.1 구조체 비트 필드를 만들고 사용하기

C99 표준에서는 비트 필드로 사용할 수 있는 자료형을 _Bool, signed int, unsigned int, int로 규정하고 있지만 대부분의 컴파일러에서는 모든 정수 자료형을 사용할 수 있습니다. 보통은 비트 필드에 부호 없는(unsigned) 자료형을 주로 사용합니다. 단, 실수 자료형은 비트 필드로 사용할 수 없습니다.

비트 필드는 다음과 같이 멤버를 선언할 때 : (콜론) 뒤에 비트 수를 지정해주면 됩니다.

struct 구조체이름 {
    정수자료형 멤버이름 : 비트수;
};

이제 구조체를 7비트, 3비트, 1비트로 나눠서 비트 필드를 정의해보겠습니다.

그림 56‑1 구조체 비트 필드

다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.

struct_bit_field.c

#include <stdio.h>

struct Flags {
    unsigned int a : 1;     // a는 1비트 크기
    unsigned int b : 3;     // b는 3비트 크기
    unsigned int c : 7;     // c는 7비트 크기
};

int main()
{
    struct Flags f1;    // 구조체 변수 선언

    f1.a = 1;      //   1: 0000 0001, 비트 1개
    f1.b = 15;     //  15: 0000 1111, 비트 4개
    f1.c = 255;    // 255: 1111 1111, 비트 8개

    printf("%u\n", f1.a);    //   1:        1, 비트 1개만 저장됨
    printf("%u\n", f1.b);    //   7:      111, 비트 3개만 저장됨
    printf("%u\n", f1.c);    // 127: 111 1111, 비트 7개만 저장됨

    return 0;
}

실행 결과

1
7
127

먼저 구조체를 정의할 때 멤버 뒤에 콜론을 붙인 뒤 비트 수를 지정합니다. 여기서 a는 1비트, b는 3비트, c는 7비트 크기로 지정했습니다.

struct Flags {
    unsigned int a : 1;    // a는 1비트 크기
    unsigned int b : 3;    // b는 3비트 크기
    unsigned int c : 7;    // c는 7비트 크기
};

이제 비트 필드에 값을 할당합니다. a에는 1, b에는 15, c에는 255를 할당했는데 비트 필드에 지정한 비트 수보다 할당한 비트 수가 많은 상태입니다.

f1.a = 1;      //   1: 0000 0001, 비트 1개
f1.b = 15;     //  15: 0000 1111, 비트 4개
f1.c = 255;    // 255: 1111 1111, 비트 8개

printf에서 %u로 각 멤버를 출력해보면 앞에서 할당한 값과 다른 값이 나오는 것을 볼 수 있습니다.

printf("%u\n", f1.a);    //   1:        1, 비트 1개만 저장됨
printf("%u\n", f1.b);    //   7:      111, 비트 3개만 저장됨
printf("%u\n", f1.c);    // 127: 111 1111, 비트 7개만 저장됨

비트 필드에는 지정한 비트 수만큼 저장되며 나머지 비트는 버려집니다. 따라서 a는 비트 그대로 1만 저장되었고, b는 비트 3개만 저장되었으므로 7, c는 비트 7개만 저장되었으므로 127이 됩니다.

다음과 같이 비트 필드의 각 멤버는 최하위 비트(Least Significant Bit, LSB)부터 차례대로 배치됩니다. 따라서 a가 최하위 비트에 오고 나머지 멤버들은 각각 상위비트에 배치됩니다.

그림 56‑2 구조체 비트 필드의 구성

sizeof 연산자로 Flags 비트 필드 구조체의 크기를 구해보면 4가 나옵니다.

struct_bit_field_sizeof.c

#include <stdio.h>

struct Flags {
    unsigned int a : 1;    // a는 1비트 크기
    unsigned int b : 3;    // b는 3비트 크기
    unsigned int c : 7;    // c는 7비트 크기
};

int main()
{
    printf("%d", sizeof(struct Flags));    // 4: 멤버를 unsigned int로 선언했으므로 4

    return 0;
}

실행 결과

4

비트 필드의 멤버를 unsigned int로 선언했으므로 구조체의 크기는 4가 됩니다. 만약 멤버를 unsigned short로 선언하면 구조체의 크기는 2가 나옵니다.

참고 | 비트 필드 비트 수 제한

다음과 같이 비트 필드의 멤버를 선언하는 자료형보다 큰 비트 수는 지정할 수 없습니다.

struct Flags {
    unsigned int a : 37;    // 컴파일 에러. unsigned int보다 큰 비트 수는 지정할 수 없음
    unsigned int b : 3;
    unsigned int c : 7;
};