https://www.it-note.kr/312

 

27. 구조체(struct) - 비트 필드(bit field)

구조체의 기능 중에서 많이 사용하지 않지만, 정수형 데이터를 비트 단위로 나누어서 사용할 수 있는 기능을 제공합니다. 이를 구조체의 bit field라고 합니다. struct 구조체명 { 정수형 멤버명1 :

www.it-note.kr

 

위 블로그 글을 정리하고 정리한 내용입니다.


 

union( 공용체 ) 관련 내용을 찾아보면서  struct 멤버 변수에 나와있는 ' : ' ( 비트 필드) 에 대해 공부했다. 

 

 

비트 필드란 우선 메모리를 보다 아끼기 위해 생긴것이다.

 

멤버변수들의 표현이 4byte 로 충분하다면 4byte 안에서 지들끼리 나눠서 사용하겠다는 목적으로 생긴것이다.

 


구조체의 기능 중에서 많이 사용하지 않지만, 정수형 데이터를 비트 단위로 나누어서 사용할 수 있는 기능을 제공합니다. 이를 구조체의 bit field라고 합니다.

 

struct 구조체명
{
    정수형 멤버명1 : 비트수;
    정수형 멤버명2 : 비트수;    
    ...
};

위의 정수형은 char, unsigned char, short, unsigned char, int, unsigned int, long, unsigned long형이 올 수 있습니다.

 

 

예). 파일 접근 권한

struct file_access
{
    unsigned short other     : 3; // rwx
    unsigned short group     : 3; // rwx
    unsigned short owner     : 3; // rwx
    unsigned short type      : 3;
    unsigned short egroup    : 1;
    unsigned short euser     : 1;
};

위의 bit field 구조체는 총 14bit의 데이터를 관리할 수 있는 형태가 된다. 이 경우 구조체의 크기는 unsigned short 타입으로 2바이트 크기를 갖게 된다.

 

예). 다른 타입이 섞여 있는 경우

struct option
{
    unsigned char  flag1 : 1;
    unsigned short flag2 : 1;   
};

위와 같이 field의 데이터 타입을 다르게 표현하여도 상관이 없으나, 구조체의 크기는 가장 큰 타입의 크기를 따르므로 2바이트를 갖게 됩니다. (X) ->  4 바이트 를 갖는다.  (이게 맞다.) 

 

예). member의 bit의 합이 데이터 타입보타 큰 경우

struct option
{
    unsigned char opt1 : 3;
    unsigned char opt2 : 6;     
};

위의 같이 field의 데이터 타입이 8bit로 1바이트인데, 전체 bit수가 8보다 크게 되면, 오류가 발생하는 것이 아니라 그 데이터 타입의 크기만큼 증가하여 2바이트를 차지하게 됩니다.

 

 

그렇다면 아래의 구조체 크기는 어떻게 될까?

 

 

아래 MSDN 을 참고하면 더 자세히 알 수 있다. 

 

https://docs.microsoft.com/ko-kr/cpp/cpp/cpp-bit-fields?view=msvc-170

 

 

C++ 비트 필드

자세한 정보: C++ 비트 필드

docs.microsoft.com


Bit Field의 응용

 

만약, IPv4의 주소를 저장하는 것을 표현한다면

#include <string.h>

struct IPv4_Addr
{
    unsigned int addr4 : 8;
    unsigned int addr3 : 8;    
    unsigned int addr2 : 8;    
    unsigned int addr1 : 8;    
};


int main()
{
    unsigned int ipaddr;
    struct IPv4_Addr addr;
    
    // ip 127.0.0.1
    addr.addr1 = 127;
    addr.addr2 = 0;
    addr.addr3 = 0;
    addr.addr4 = 1;
    
    memcpy(&ipaddr, &addr, sizeof(unsigned int));
}

형식으로 표현할 수 있으며, addr1 ~ addr4까지는 각각 8bit씩 차지하므로 전체 크기는 4바이트를 차지하는 구조체가 됩니다. 먼저 선언한 변수가 하위 bit 부분이 됩니다. 그리고 이것을 unsigned int 변수에 복사를 하면 bit 연산자로 하지 않고 쉽게 처리할 수 있습니다.

 

 


 

조합형 한글을 표현하는 방식으로 이 bit field 를 사용 할 수 도 있다.

 

typedef struct 
{
    unsigned short end : 5;     // 종성
    unsigned short mid : 5;     // 중성
    unsigned short first : 5;   // 초성
    unsigned short han_yn : 1;  // 한글비트 1이면 한글, 그렇지 않으면 한글 아님
};  

 

이를 통하여 한글의 제자원리를 완벽하게 지원하는 형태였는 데, 요즘은 모두 완성형의 형태로 사용하고 있다.

 


 

또한, 공용체(union)과 더불어 CPU의 register 메모리 AH, AL, AX 등의 표현을 쉽게 설정하고 이 값들을 BIOS 인터럽트 번호로 전달하여 하드웨어 제어를 하는 초창기의 PC에서 많이 사용되었습니다. (16bit 컴퓨터 시절...)

union AX
{
    unsigned short ax;             // AX register의 전체 크기
    struct {
    	unsigned short al : 8;     // register의 하위 바이트
    	unsigned short ah : 8;     // register의 상위 바이트
    };
};

 


 

 

부동소숫점을 표현하기 위해서 double이라는 변수를 사용합니다. 이들은 만약 구현을 해야 한다면 64비트 크기의 데이터를 비트 단위로 영역을 나누어서 그 의미를 부여하고 있으므로 과도한 비트 연산을 진행해야할 것입니다.

 

#include <stdio.h>
#include <string.h>

struct Double
{
    unsigned long mant : 52;
    unsigned long exp  : 11;
    unsigned long sign : 1;
} Double;

int main(void)
{
    double d;
    struct Double s;
    
    d = -1234567890123456;
    
    memcpy(&s, &d, 8);
    
    printf("sign = %u\n",  s.sign);
    printf("exp  = %u\n",  s.exp);
    printf("mant = %lu\n",  s.mant);
    
    
    return 0;
}

위와 같이 double형의 데이터에 대해서 sign, 지수부, 가수부의 데이터가 어떤형식으로 저장되어 있는 지 알아볼 수 있습니다.

위의 s.sign의 경우는 1비트 값을 가지므로 0 또는 1만 대입할 수 있으며, 그 이외의 데이터를 저장하려고 하위 1비트 이외의 값은 버려집니다.

 

+ Recent posts