Language/C [C언어] 구조체 비트필드/공용체를 함께 사용하기
  • 728x90
    반응형

     

     

     

    구조체 비트 필드/공용체를 함께 사용하기


     

     

     

     

    목차

       

       

       

       

       

       

      구조체 비트 필드

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

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

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

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

       

      문법

      stuct 구조체 이름 {

          정수자료형 멤버이름 : 비트수;

      };

       

      이제 구조체를 7비트, 3비트, 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:11 1111, 비트 7개만 저장됨

       

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

       

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

       

      구조체 비트 필드의 구성

       

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

       

      #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;
      };

       

       

       

       

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

      보통 사람이 코드에서 값을 지정할 때는 비트 필드를 사용하지만 CPU나 칩에 값을 설정할 때는 모든 비를 묶어서 한꺼번에 저장한다. 이번에는 비트 필드의 값을 한꺼번에 사용할 수 있도록 비트 필드와 공용체를 함께 사용해본다.

       

       

      비트 필드와 공용체

       

      struct_union_bit_field.c

      #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;
      }

       

      실행 결과

      64020

       

       

      🔔 예제)

      먼저 비트 필드로 사용할 멤버는 익명 구조체로 감싸준다. 그리고 비트 필드의 값을 한꺼번에 접근할 수 있도록 unsigned short 형 멤버를 선언하고 익용 공용체로 감싸준다. 여기서는 2바이트(16비트) 크기의 unsigned short에 정확히 맞추기 위해 비트 수를 3, 2, 7, 4로 지정했다.

      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비트)
      	};
      };

       

      이제 비트 필드에 값을 할당한다. 여기서는 각 비트 수에 맞게 값을 할당했다.

       

      🔔 예제)

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

       

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

       

      🔔 예제)

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

       

      비트 필드와 공용체의 구성

       

       

       

       

       

       

       

      728x90
      반응형
    상단으로