F.R.I.D.A.Y.

구조체(struct) part2. 비트 필드 본문

DEV/C C++

구조체(struct) part2. 비트 필드

F.R.I.D.A.Y. 2020. 1. 29. 22:59
반응형

 이번 시간에는 구조체에서 사용하는 비트 필드에 대해 알아봅니다. 원래 구조체를 설명하고 곧바로 시작할 생각이었는데 공용체 문법에서 먼저 나오게 되었네요 :|

 

# 비트 필드 연산자는 공용체에서도 사용할 수 있어요.


정수 자료형

 C언어 자체에서 제공하는 정수 자료형은 기본적으로 다섯 가지[# char, short, int, long, long long]입니다. 주로 int를 사용하지만 가끔씩 long long 자료형을 사용하기도 하지요.

 

 long long은 64비트 자료형으로 그 크기는 이미지와 같이 거대한 값을 저장할 수 있습니다..

Min : -9,223,372,036,854,775,808[# 약 -920경]

Max : +9,223,372,036,854,775,807[# 약 920경]

 

 그러나 우리는 이렇게 거대한 양의 정보를 다룰 일이 많이 없습니다. 피보나치 정도 되면 몇 번만 넘어가도 금세 뛰어넘기는 하지만요.


비트 필드 연산자

 이럴 때 비트 필드 연산자를 이용할 수 있습니다. 비트 필드 연산자는 자료형의 사용할 비트를 제한할 수 있습니다. 사용은 아래처럼 할 수 있습니다.

struct data{
    long long value:48;
};

 data 구조체의 멤버 변수로 LLong 타입의 value를 선언했습니다. 그리고 비트 필드 연산자를 작성한 후 그 오른쪽에 48을 입력했습니다. 이렇게 작성하면 실제 value의 크기는 64비트이지만, 오른쪽에 입력한 48을 따라서 전체 64비트 중 48비트만 이용하겠다는 것을 의미합니다.

 비트 필드 연산자를 이용해 제한한 경우에는 LSB부터 비트를 사용하게 됩니다. 즉, 0번 비트부터 차례로 쌓아서 47번 비트까지 사용하는 것입니다.

 

 그렇다면 궁금합니다. 48번부터  63번 비트는 이용하지 못하는 것일까요? 영영? 아닙니다. 만일 비트 필드 연산자를 추가로 이용하면 이 영역을 또 사용할 수 있습니다.

struct data{
    long long value:48;
    long long rest:16;
};

 이렇게 사용하면 value가 앞선 0~47번 비트를, rest가 이후의 48~63번 비트를 이용합니다.

 다만, 하나의 공간 long long을 이어서 사용하려면 이어서 오는 자료형이 같아야 합니다. 즉, value 멤버 다음에 오는 rest 멤버의 자료형이 long long이 아니라면 별개의 공간으로 취급해 관리합니다.

struct data{
    long long value:48;
    long rest:16;
};

 이 코드의 경우에는 8Byte 공간 다음 4Byte[# long] 중 16비트를 rest가 사용하도록 하는 코드입니다.

 

 또한, 자료형을 같게 하고 이어서 작성하더라도 연달아 나오는 멤버들이 사용하는 비트 수의 합이 자료형을 넘길 경우 넘겨진 부분부터 새로이 공간이 할당되어 처리됩니다.

struct data{
    long long value:48;
    long long rest:17; // 새로운 8Byte에 위치
};
더보기

# 새 공간에 할당되는 것을 확인하는 방법

 아래 코드를 실행해봅니다. 비트 합이 클 경우 자료형 비트 수를 넘지 않는 변수를 하나의 메모리에 담는다는 것을 알 수 있습니다.

#include <stdio.h>
struct data{
    unsigned char _1:1;
    unsigned char _2:1;
    unsigned char _3:1;
    unsigned char _4:1;
    unsigned char _5:1;
    unsigned char _6:1;
    unsigned char _7:1;
    unsigned char _8:2;
    unsigned char _9:1;
    
};

int main(void){
    struct data t = {0,1,0,0,
                     0,0,0,3,
                     1};
    
    printf("%lu\n", sizeof(struct data));

    printf("%hhu %hhu %hhu %hhu | %hhu %hhu %hhu %hhu | %hhu \n", 
            t._1, t._2, t._3, t._4, t._5, t._6, t._7, t._8, t._9);
            
    printf("%hhu %hhu\n",*(unsigned char *)&t, *((unsigned char *)&t + 1));
    return 0;
}

 struct data의 멤버 _1부터 _8까지는 총 9비트로 unsigned char 타입에 담을 수 있는 멤버는 _7까지입니다. 따라서 _8부터는 새로운 1바이트 메모리를 만들어 접근합니다.

 이미지처럼 특정 멤버를 더했을 때 자료형을 넘게 되면 해당 멤버부터는 새로운 크기를 받아서 접근을 하게 됩니다. 그럼 0x100번지의 MSB와 0x101번지의 4-7번 비트는 어떻게 되냐고요? 사용하지 않고 버려지게 됩니다. 물론, 포인터 접근을 하면 사용 가능[#오류? 발생하지 않습니다. 운영체제의 메모리 관리 최소 단위는 1Byte입니다. 2바이트를 할당한 상태에서 변수가 침범하지 못하게 막아두었을 뿐 프로그램에 할당받은 메모리이므로 포인터 접근으로 값을 수정하더라도 오류는 발생하지 않습니다.]합니다.

    *((unsigned char *)&t + 1) |= 1 << 5;

 위 코드는 0x101번지의 5번 비트를 1로 변경하는 코드입니다.

 그리고 값을 출력해보면 39가 나오게 됩니다. 2^5[# 1 << 5] + 2^2[# 코드상 마지막 초기화 값] + 2^1 + 2^0[# 코드상 초기화 값 3]의 결과이죠.

결과

자료형보단 작게

 애초에 비트 필드 연산자는 특정 자료형을 제한해서 사용하는 연산자입니다. 따라서 비트 필드 연산자로 변수를 선언할 때는 아래와 같이 본연의 크기보다 크게 만들 수 없습니다.

struct data{
    long long value:65; // LLong 크기인 64비트보다 1비트도 크게 만들 수 없습니다.
};

비트 필드를 사용하는 곳

 비트 필드 연산자는 비트 연산자를 모를 때 구조체로 묶어서 특정 크기의 비트 패턴을 일일이 나열시킬 수 있습니다. 또한, 크기가 작은 값을 저장할 때 보기 쉽게 저장할 수 있습니다.

 

비트 연산자 : 함수에 인자 넘기기

비트 연산자 : 메모리 크기 줄이기 최근 비트 연산자에 대한 질문을 들어온지라, 오늘은 비트 연산자에 대해 알아봅니다. 더보기 # 들어가기에 앞서.. 비트 연산자는 프로그래밍에 있어 고급 기술이라 분류할 수..

pang2h.tistory.com

 이 글에서 비트 연산자를 활용해 여러 속성 값을 하나의 인자로 넘기는 방법에 다뤘었습니다. 이 방식은 편리하지만 잘 모르는 사람들이 볼 때는 오히려 사용하는 데 어려움을 겪을 수 있겠죠. 그러나 비트 필드 연산자를 활용하면 비트 연산자를 활용할 때보다는 좋지 않을지언정 더 나을 코드 표기를 보일 수  있습니다.

struct style{
    unsigned int showTitleBar:1;
    unsigned int showCloseBtn:1;
    unsigned int showMaximumBtn:1;
    .
    .
    .
};

 이런 식으로 구조체를 작성하게 되면 굳이 비트 연산자로 접근하지 않아도 각각의 비트를 구조체 멤버 변수에 접근하듯이 사용할 수 있습니다. 이름이 정해져 버리는 한계가 존재하더라도 오히려 사용함에 있어서는 더 편리할 수 있겠죠.

# index

728x90
반응형

'DEV > C C++' 카테고리의 다른 글

포인터(pointer) part4. 함수 포인터 배열  (0) 2020.01.30
포인터(pointer) part3. 함수 포인터  (0) 2020.01.30
공용체(union) part1. default  (0) 2020.01.28
포인터의 크기  (0) 2020.01.26
포인터 배열? 배열 포인터?  (0) 2020.01.26
Comments