profile image

L o a d i n g . . .

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

구조체 비트 필드를 사용하면 구조체 멤버를 비트 단위로 저장할 수 있다.

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

 

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

대부분의 컴파일러에서는 모든 정수 자료형을 사용할 수 있다. 보통은 비트 필드에 부호 없는(unsigned) 자료형을 주로 사용한다. 단, 실수 자료형은 비트 필드로 사용할 수 없다.

 

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

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

 

#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

 

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

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

 

구조체 비트 필드의 구성

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

 

 

56.2 비트 필드와 공용체를 함께 사용하기

이번에는 비트 필드의 값을 한꺼번에 사용할 수 있도록 비트 필드와 공용체를 함께 사용해보겠다.

#include <stdio.h>

struct Flags {
    union {    // 익명 공용체
        struct {    // 익명 구조체
            unsigned short a : 3;    // a는 3비트 크기
            unsigned short b : 2;    // b는 2비트 크기
            unsigned short c : 7;    // c는 7비트 크기
            unsigned short d : 4;    // d는 4비트 크기
        };                           // 합계 16비트
        unsigned short e;    // 2바이트(16비트)
    };
};

int main()
{
    struct Flags f1 = { 0, };    // 모든 멤버를 0으로 초기화

    f1.a = 4;     //  4: 0000 0100
    f1.b = 2;     //  2: 0000 0010
    f1.c = 80;    // 80: 0101 0000
    f1.d = 15;    // 15: 0000 1111

    printf("%u\n", f1.e);    // 64020: 1111 1010000 10 100

    return 0;
}

먼저 비트 필드로 사용할 멤버는 익명 구조체로 감싸준다. 그리고 비트 필드의 값을 한꺼번에 접근할 수 있도록 unsigned short형 멤버를 선언하고 익명 공용체로 감싸준다.

비트 필드와 공용체

공용체로 감싸준 멤버 e printf로 출력해보면 64020이 나온다. 즉, 비트 필드에 할당한 비트들을 차례대로 연결하면 1111 1010000 10 100이 되므로 10진수로 표현했을 때 64020이 된다.

 

 

 

Unit 57. 열거형 사용하기

열거형을 사용하면 정수형 상수를 좀 더 편하게 정의할 수 있다.

 

57.1 열거형 정의하기

열거형은 enum 키워드를 사용하여 정의하며 열거, 목록을 뜻하는 enumeration에서 따왔다.

enum 열거형이름 {
    값1 = 초깃값,
    값2,
    값3
};

열거형은 정의만 해서는 사용을 할 수가 없다. 따라서 열거형도 변수로 선언해서 사용한다.

  • enum 열거형이름 변수이름;

 

#include <stdio.h>

enum DayOfWeek {    // 열거형 정의
    Sunday = 0,         // 초깃값 할당
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday
};

int main()
{
    enum DayOfWeek week;    // 열거형 변수 선언

    week = Tuesday;    // 열거형 값 할당

    printf("%d\n", week);   // 2: Tuesday의 값 출력

    return 0;
}

 

열거형의 값은 처음에만 할당해주면 그 아래에 오는 값들은 1씩 증가하면서 자동으로 할당된다(아무 값도 할당하지 않으면 0부터 시작). 따라서 처음에 오는 Sunday에 0을 할당하면 Monday는 1, Tuesday는 2, Wednesday는 3이 된다.

 

보통 열거형 변수에는 미리 정의한 열거형 값을 넣는다. 여기서도 열거형 변수 week에 Tuesday 값을 넣었다.

 

※ 참고 : 문법으로 정해진 규칙은 아니지만 열거형 이름이나 값을 정의할 때 대문자만 사용하는 경우가 많다. 특히 단어와 단어 사이에는 _을 주로 사용한다.

 

 

57.2 열거형을 switch에 활용하기

열거형은 switch 분기문을 사용할 때 유용하다.

switch (열거형변수)
{
case 열거형값:
    실행할코드;
    break;
}

 

#include <stdio.h>

enum LuxSkill {
    LightBinding = 1,
    PrismaticBarrier,
    LucentSingularity,
    FinalSpark
};

int main()
{
    enum LuxSkill skill;    // 열거형 변수 선언

    skill = LightBinding;    // 열거형 값 할당

    switch (skill)
    {
    case LightBinding:         // 열거형 값이 LightBinding일 때
        printf("LightBinding\n");
        break;
    case PrismaticBarrier:     // 열거형 값이 PrismaticBarrier일 때
        printf("PrismaticBarrier\n");
        break;
    case LucentSingularity:    // 열거형 값이 LucentSingularity일 때
        printf("LucentSingularity\n");
        break;
    case FinalSpark:           // 열거형 값이 FinalSpark일 때
        printf("FinalSpark\n");
        break;
    default:
        break;
    }

    return 0;
}

열거형 변수를 switch 분기문에 사용하면 열거형 값에 따라 코드를 실행할 수 있다.

열거형을 사용하면 LightBinding, PrismaticBarrier, LucentSingularity, FinalSpark와 같이 스킬 이름으로 처리할 수 있다.

 

 

57.3 열거형을 for에 활용하기

다음은 일요일부터 토요일까지 매일 반복되는 알람을 설정할 때 열거형과 반복문을 사용하는 코드이다.

#include <stdio.h>

typedef enum _DayOfWeek {    // 열거형 이름은 _DayOfWeek
    Sunday = 0,                  // 초깃값을 0으로 할당
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    DayOfWeekCount               // 열거형 값의 개수를 나타내는 항목 추가
} DayOfWeek;                 // typedef를 사용하여 열거형 별칭을 DayOfWeek로 정의

int main()
{
    // 초깃값은 Sunday, i가 DayOfWeekCount보다 작을 때까지만 반복
    for (DayOfWeek i = Sunday; i < DayOfWeekCount; i++) 
    {
        printf("%d\n", i);
    }

    return 0;
}

for 반복문에서 열거형 별칭으로 변수 i를 선언한 뒤 초깃값으로 Sunday를 넣는다. 그리고 i DayOfWeekCount 보다 작을 때까지만 반복한다.

-> 열거형 값 Sunday부터 Saturday까지 반복할 수 있다.

복사했습니다!