비트 필드
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
비트 필드는 프로그래밍에서 여러 비트를 하나의 단위로 묶어 관리하는 기법을 의미한다. C/C++에서는 구조체 내에서 각 멤버의 비트 수를 지정하여 비트 필드를 구현할 수 있으며, 비트 연산을 통해 직접 비트 단위의 조작도 가능하다. 비트 필드는 메모리 공간을 절약하고, 하드웨어 레지스터나 장치 제어와 같은 특정 상황에서 유용하게 사용되지만, 엔디안 문제, 컴파일러 최적화, 스레드 안전성 등의 단점도 존재한다. 임베디드 시스템, 운영체제 등 다양한 분야에서 활용되며, 게임 컨트롤러 상태 표현, 프로세서 상태 레지스터 등에 예시가 있다.
더 읽어볼만한 페이지
- 비트 자료 구조 - 비트맵
비트맵은 픽셀 정보를 비트 값으로 저장하는 이미지 파일 형식으로, 색상 깊이에 따라 비트 수를 할당하며, 마이크로소프트의 DIB 형식이 널리 사용되고 GIF, PNG, JPEG 등 압축 형식도 존재한다.
비트 필드 | |
---|---|
기본 정보 | |
![]() | |
유형 | 데이터 구조 |
설명 | 메모리 내에서 하나 이상의 비트를 할당하여 값을 저장하는 데 사용되는 컴퓨터 프로그래밍의 데이터 구조이다. |
활용 | 낮은 수준의 장치 제어, 프로토콜, 암호화, 압축 및 체크섬과 같은 작업을 위한 데이터 레이아웃을 생성하는 데 유용하다. |
세부 정보 | |
저장 단위 | 비트 |
접근 방식 | 직접 접근 |
설계 목적 | 메모리 사용 최적화 |
특징 | |
컴파일러 의존성 | 비트 필드의 메모리 할당 방식은 컴파일러에 따라 달라질 수 있다. |
이식성 문제 | 비트 필드를 사용하는 코드는 다른 시스템으로 이식할 때 문제가 발생할 수 있다. |
메모리 절약 | 작은 크기의 데이터를 저장할 때 메모리를 절약할 수 있다. |
접근 복잡성 | 비트 필드에 접근하는 것은 일반적인 변수에 접근하는 것보다 복잡할 수 있다. |
구현 | |
사용 언어 | C C++ |
예시 | 구조체 또는 클래스 내에서 정의 |
2. 구현
비트 필드는 C/C++ 등의 프로그래밍 언어에서 struct를 사용하거나, 비트 연산(AND, OR, XOR, 시프트 연산 등)을 통해 직접 구현할 수 있다.[5] C에서는 `int`, `unsigned int`, `signed int` 등의 타입을 사용할 수 있고, C99 표준부터는 `_Bool`, C23 표준부터는 `_BitInt(N)` 타입도 사용할 수 있다. C++에서는 모든 정수 또는 열거형 타입을 사용할 수 있으며, 대부분의 C 컴파일러도 이를 허용한다.
구조체를 사용하는 방식과 비트 연산을 이용하는 방식에 대한 자세한 내용은 C/C++ 구조체 방식 및 비트 연산 방식 하위 섹션을 참고하라.
2. 1. C/C++ 구조체 방식
C/C++에서는 구조체의 멤버 변수를 선언할 때 콜론(:) 뒤에 비트 수를 지정하는 방식으로 비트 필드를 선언할 수 있다.[6]```c
struct preferences {
unsigned int likes_ice_cream : 1;
unsigned int plays_golf : 1;
unsigned int watches_tv : 1;
unsigned int reads_books : 1;
};
struct preferences fred;
fred.likes_ice_cream = 1;
fred.plays_golf = 1;
fred.watches_tv = 1;
fred.reads_books = 0;
if (fred.likes_ice_cream == 1) {
/* ... */
}
```
같은 타입으로 인접하게 선언된 비트 필드들은 컴파일러에 의해 더 적은 수의 워드로 압축되어 저장될 수 있다. 이는 각 필드가 개별적으로 선언되었을 때 사용되는 메모리와 비교하여 더 효율적이다.[6]
하지만 구조체의 비트 멤버는 몇 가지 단점이 있다. 우선, 메모리 상의 비트 순서(엔디안)는 컴파일러 및 대상 프로세서 아키텍처에 따라 달라진다.[12] 또한, 많은 컴파일러가 비트 멤버의 읽기/쓰기에 대해 비효율적인 코드를 생성하는 경우가 있다. (프로세서의 워드 단위로 로드/저장하는 것이 더 효율적이다).
멀티 프로세서 시스템에서는 스레드 안전성 문제가 발생할 수 있다. 대부분의 CPU는 메모리 상의 임의의 비트 집합을 처리할 수 없고, 최소한 바이트 단위로만 어드레싱이 가능하기 때문이다. 다음은 뮤텍스를 사용하더라도 스레드 안전하지 않은 코드 예시이다.
```c
struct foo {
int flag : 1;
int counter : 15;
};
struct foo my_foo;
pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
/* 한 스레드에서 실행되는 함수 */
void func1(void) {
pthread_mutex_lock(&my_mutex);
my_foo.flag = !my_foo.flag;
pthread_mutex_unlock(&my_mutex);
}
/* 다른 스레드에서 실행되는 함수 */
void func2(void) {
my_foo.counter += 1;
}
```
대부분의 CPU에서 `flag`와 `counter`를 별도로 로드하고 저장하는 것은 하드웨어 레벨에서 불가능하다. 따라서 스레드 안전성을 확보하려면 `flag`와 `counter` 모두에 접근하기 전후에 뮤텍스를 잠그고 해제해야 한다. 비트 필드의 정확한 구조는 워드 크기에 따라 달라질 수 있다는 점도 고려해야 한다.
2. 2. 비트 연산 방식
비트 필드를 지원하지 않는 언어를 사용하거나, 비트 표현을 직접 제어하고 싶을 때는 비트 연산을 사용하여 비트 필드를 구현할 수 있다. AND, OR, XOR, 시프트 연산 등을 사용하여 특정 비트를 설정, 해제, 검사, 반전할 수 있다.[7]C/C++(C++)에서는 부호 있는 정수에 대한 비트 연산은 구현체 정의 동작이 되는 경우가 있으므로, 부호 없는 정수를 사용해야 한다.[9]
2의 거듭제곱을 나타내기 위해 `0x08`과 같은 하드 코딩된 숫자를 사용하는 대신, `1 << 3`과 같은 비트 시프트 연산자를 사용한 표현을 사용하면, 비트 마스크를 나타낸다는 프로그래머의 의도가 명확해지고 가독성 측면에서 바람직하다. 또한, C++14 이후에는 `0b` 접두사를 붙여 `0b00001000`과 같은 2진수 리터럴을 기술할 수도 있다.[10] C23에도 유사한 2진수 리터럴 표기가 도입될 예정이다.[11]
C 언어에서의 구현 예시는 다음과 같다.
```c
#define KEY_UP (1u << 0u)
#define KEY_RIGHT (1u << 1u)
#define KEY_DOWN (1u << 2u)
#define KEY_LEFT (1u << 3u)
#define KEY_BUTTON1 (1u << 4u)
#define KEY_BUTTON2 (1u << 5u)
unsigned int gameControllerStatus = 0;
void setKeyPressed(unsigned int key) {
gameControllerStatus |= key;
}
void setKeyReleased(unsigned int key) {
gameControllerStatus &= ~key;
}
unsigned int isKeyPressed(unsigned int key) {
return gameControllerStatus & key;
}
3. 예제
비트 필드는 여러 개의 작은 정수 변수가 필요할 때 메모리 소비를 줄이는 데 사용될 수 있다. 예를 들어, 정수 값을 저장하는 데 보통 2바이트(16비트)가 필요하지만, 실제로는 1~2비트만 필요한 경우가 있다. 이러한 작은 변수들을 비트 필드로 묶으면 메모리를 효율적으로 사용할 수 있다.[5]
C에서는 `int`, `unsigned int`, `signed int` 등을 사용하여 비트 필드를 만들 수 있다. C++에서는 정수 또는 열거형 타입을 사용할 수 있다. 프로그래머는 비트 필드 구조체를 선언하여 각 필드의 너비를 지정할 수 있다.[6] 인접하게 선언된 비트 필드는 컴파일러에 의해 더 적은 수의 워드로 압축될 수 있다.
C/C++에서는 부호 있는 정수에 대한 비트 연산은 구현체 정의 동작이 되는 경우가 있으므로, 부호 없는 정수를 사용해야 한다.[9]
브라이언 W. 커니핸과 데니스 M. 리치는 프로그래밍 언어 C에서 비트 필드를 직접 정의하고 접근하는 방법을 제시했다. 이 방법을 사용하면 비트 연산자가 불필요해지고, 구조체 멤버처럼 비트 멤버에 접근할 수 있다.
```c
struct preferences {
unsigned int likes_ice_cream : 1;
unsigned int plays_golf : 1;
unsigned int watches_tv : 1;
unsigned int reads_books : 1;
};
```
위와 같이 `struct`를 사용하여 비트 필드를 선언하고, `fred.likes_ice_cream = 1;`과 같이 구조체 멤버처럼 접근할 수 있다.
하지만 구조체의 비트 멤버는 다음과 같은 실용적인 결점이 있다.
- 엔디안: 메모리 상의 비트 순서는 컴파일러 및 대상 프로세서 아키텍처에 따라 달라진다.[12]
- 많은 컴파일러는 비트 멤버 읽기/쓰기에 대해 비효율적인 코드를 생성한다.
- 멀티 프로세서 시스템에서 비트 필드는 스레드 안전성 문제를 일으킬 수 있다.
```c
struct foo {
int flag : 1;
int counter : 15;
};
```
위의 코드에서 대부분 CPU는 `flag`와 `counter`를 별도로 로드 및 저장하는 것이 하드웨어 레벨에서 불가능하므로, 스레드 안전을 위해서는 추가적인 조치가 필요하다.
더 자세한 내용은 "C/C++ 코드 예제" 하위 섹션을 참조하라.
3. 1. C/C++ 코드 예제
C/C++에서 비트 필드를 선언하는 방법은 다음과 같다.[6]```c
// 불투명 여부와 표시 여부
#define YES 1
#define NO 0
// 선 스타일
#define SOLID 1
#define DOTTED 2
#define DASHED 3
// 기본 색상
#define BLUE 0b100
#define GREEN 0b010
#define RED 0b001
// 혼합 색상
#define BLACK 0
#define YELLOW (RED | GREEN) /* 011 */
#define MAGENTA (RED | BLUE) /* 101 */
#define CYAN (GREEN | BLUE) /* 110 */
#define WHITE (RED | GREEN | BLUE) /* 111 */
const char* colors[8] = {"Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White"};
// 비트 필드 상자 속성
struct BoxProps
{
unsigned int opaque : 1;
unsigned int fill_color : 3;
unsigned int : 4; // 8비트로 채움
unsigned int show_border : 1;
unsigned int border_color : 3;
unsigned int border_style : 2;
unsigned char : 0; // 가장 가까운 바이트(16비트)로 채움
unsigned char width : 4, // 바이트를 2개의 4비트 필드로 분할
height : 4;
};
```
C `struct`에서 비트 필드의 레이아웃은 구현 정의되므로, 컴파일러 간 예측 가능한 동작을 유지하려면, 원시 데이터형과 비트 연산자를 사용하여 비트 필드를 에뮬레이션하는 것이 좋다.
브라이언 W. 커니핸과 데니스 M. 리치의 저서 『프로그래밍 언어 C』에서는, 직접 필드를 정의하고 접근하는 방법을 제시하며, 이 방법을 사용하면 비트 연산자가 불필요해지고, 비트 멤버에 구조체 멤버와 마찬가지로 접근할 수 있다고 설명한다.
```c
struct preferences {
unsigned int likes_ice_cream : 1;
unsigned int plays_golf : 1;
unsigned int watches_tv : 1;
unsigned int reads_books : 1;
};
struct preferences fred;
fred.likes_ice_cream = 1;
fred.plays_golf = 1;
fred.watches_tv = 1;
fred.reads_books = 0;
if (fred.likes_ice_cream == 1) {
/* ... */
}
```
하지만, 구조체의 비트 멤버는 실용적인 결점이 있다.
- 메모리 상의 비트 순서, 즉 엔디안은 컴파일러 (및 대상 프로세서 아키텍처)에 따라 달라진다.[12]
- 많은 컴파일러는 비트 멤버 읽기/쓰기에 대해 비효율적인 코드를 생성한다.
- 비트 필드는 멀티 프로세서 시스템의 경우 스레드 안전성 문제가 발생할 수 있다. 대부분의 CPU는 메모리 상 임의의 비트 집합을 처리할 수 없고, 최소 바이트 단위 어드레싱만 가능하기 때문이다.
다음 코드는 뮤텍스를 사용하더라도 스레드 안전하지 않다.
```c
struct foo {
int flag : 1;
int counter : 15;
};
struct foo my_foo;
pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
/* 한 스레드에서 실행되는 함수 */
void func1(void) {
pthread_mutex_lock(&my_mutex);
my_foo.flag = !my_foo.flag;
pthread_mutex_unlock(&my_mutex);
}
/* 다른 스레드에서 실행되는 함수 */
void func2(void) {
my_foo.counter += 1;
}
```
대부분 CPU에서 `flag`와 `counter`를 별도 로드 및 저장하는 것은 하드웨어 레벨에서 불가능하며, 스레드 안전을 위해서는 `counter` 자체가 스레드 안전할 필요가 없더라도, "flag와 counter 양쪽"에 대해 모든 접근 전후에 뮤텍스를 잠그고 해제해야 한다. 비트 순서에 따라 비트 필드의 정확한 구조는 워드 크기에 따라 달라진다.
3. 1. 1. 게임 컨트롤러 상태 표현
c/* 이 전처리기 지시문 각각은 컨트롤러의 버튼 하나에 해당하는 단일 비트를 정의합니다.
버튼 순서는 닌텐도 엔터테인먼트 시스템과 일치합니다. */
#define KEY_RIGHT 0b00000001
#define KEY_LEFT 0b00000010
#define KEY_DOWN 0b00000100
#define KEY_UP 0b00001000
#define KEY_START 0b00010000
#define KEY_SELECT 0b00100000
#define KEY_B 0b01000000
#define KEY_A 0b10000000
unsigned char gameControllerStatus = 0;
/* OR을 사용하여 gameControllerStatus 설정 */
void KeyPressed(unsigned char key)
{
gameControllerStatus |= key;
}
/* AND 및 ~ (이진 NOT)을 사용하여 gameControllerStatus 지우기 */
void KeyReleased(unsigned char key)
{
gameControllerStatus &= ~key;
}
/* AND를 사용하여 비트가 설정되었는지 테스트 */
unsigned char IsPressed(unsigned char key)
{
return gameControllerStatus & key;
}
```
위 코드는 게임 컨트롤러의 각 버튼 상태를 비트 필드로 표현하는 예시이다. 각 버튼은 하나의 비트에 해당하며, `KEY_RIGHT`, `KEY_LEFT` 등과 같이 정의되어 있다.[9]
`KeyPressed` 함수는 해당 버튼이 눌렸을 때 `gameControllerStatus` 변수에서 해당 비트를 설정(OR 연산)한다. `KeyReleased` 함수는 버튼에서 손을 뗐을 때 해당 비트를 지운다(AND 및 NOT 연산). `IsPressed` 함수는 특정 버튼이 눌려 있는지 확인(AND 연산)한다.[9]
C++14 이후에는 `0b` 접두사를 붙여 `0b00001000`과 같은 2진수 리터럴을 기술할 수 있다.[10] 비트 시프트 연산자 (`<<`)를 사용하여 비트 마스크를 표현할 수 있다. (예: `1 << 3`은 네 번째 비트(0부터 시작)를 나타내는 비트 마스크).[11]
C/C++에서는 부호 있는 정수에 대한 비트 연산은 구현체 정의 동작이 되는 경우가 있으므로, 부호 없는 정수를 사용해야 한다.[9]
3. 1. 2. 프로세서 상태 레지스터
프로세서의 상태 레지스터는 여러 플래그 비트로 구성된 비트 필드이다.[8] 각 플래그 비트는 프로세서의 현재 상태에 대한 정보를 나타낸다. 예를 들어, 6502 프로세서의 상태 레지스터는 다음과 같다.비트 7 | 비트 6 | 비트 5 | 비트 4 | 비트 3 | 비트 2 | 비트 1 | 비트 0 |
---|---|---|---|---|---|---|---|
N (음수 플래그) | oV (오버플로 플래그) | - | B (브레이크 플래그) | D (십진 플래그) | I (인터럽트 비활성화 플래그) | Z (제로 플래그) | C (캐리 플래그) |
이 비트들은 연산 결과에 따라 프로세서에 의해 설정된다. 특정 비트(예: 캐리, 인터럽트 비활성화, 십진 플래그)는 설정 및 해제 명령을 사용하여 명시적으로 제어할 수 있다. 또한, 분기 명령은 플래그의 현재 상태에 따라 실행을 변경하도록 정의된다.
예를 들어, `ADC` (Carry 포함 덧셈) 명령 후, `BVS` (오버플로 설정 시 분기) 명령을 사용하여 덧셈 명령의 결과에 따라 프로세서에 의해 오버플로 플래그가 설정되었는지 여부에 따라 분기할 수 있다.
3. 2. 비트 추출 및 변경
마스킹 및 비트 연산(AND, OR, XOR)을 사용하여 특정 비트를 추출하거나 변경할 수 있다.[7]플래그 필드 내 플래그의 하위 집합은 AND 연산을 마스크와 수행하여 추출할 수 있다. 많은 수의 프로그래밍 언어는 `1 << n`이 단일 비트를 n번째 위치로 정렬하는 시프트 연산자(<<)를 지원한다. 대부분의 언어는 또한 하나 이상의 비트 값을 분리하기 위해 AND 연산자(&)를 사용하는 것을 지원한다.
장치의 상태 바이트가 0x67이고 5번째 플래그 비트가 데이터 준비됨을 나타내는 경우, 마스크 바이트는 `2^5 = 0x20`이다. 상태 바이트 0x67(2진수로 `0110 0111`)을 마스크 바이트 0x20(2진수로 `0010 0000`)과 AND 연산하면 0x20이 된다. 이는 플래그 비트가 설정되었음을 의미하며, 즉, 장치가 데이터를 준비했음을 의미한다. 플래그 비트가 설정되지 않았다면, 이 값은 0이 되어 장치에서 사용할 수 있는 데이터가 없음을 나타낸다.
변수 '''v'''에서 '''n'''번째 비트를 확인하려면 다음 중 하나를 수행한다(둘 다 동일하다).
- `bool nth_is_set = ('''v''' & (1 << '''n''')) != 0;`
- `bool nth_is_set = ('''v''' >> '''n''') & 1;`
플래그의 비트를 쓰거나, 읽거나, 토글하는 작업은 OR, AND, NOT 연산만을 사용하여 수행할 수 있으며, 이러한 연산은 프로세서에서 빠르게 수행될 수 있다. 비트를 설정하려면 상태 바이트와 마스크 바이트를 OR 연산한다. 마스크 바이트 또는 상태 바이트에 설정된 모든 비트는 결과에 설정된다.
비트를 토글하려면 상태 바이트와 마스크 바이트를 XOR 연산한다. 이렇게 하면 비트가 지워져 있으면 설정되고, 설정되어 있으면 지워진다.
C 언어에서의 구현 예시는 다음과 같다.
```c
#define KEY_UP (1u << 0u)
#define KEY_RIGHT (1u << 1u)
#define KEY_DOWN (1u << 2u)
#define KEY_LEFT (1u << 3u)
#define KEY_BUTTON1 (1u << 4u)
#define KEY_BUTTON2 (1u << 5u)
unsigned int gameControllerStatus = 0;
void setKeyPressed(unsigned int key) {
gameControllerStatus |= key;
}
void setKeyReleased(unsigned int key) {
gameControllerStatus &= ~key;
}
unsigned int isKeyPressed(unsigned int key) {
return gameControllerStatus & key;
}
4. 활용 분야
비트 필드는 프로그램에서 여러 정수 변수가 적은 메모리 공간을 차지해야 할 때 유용하게 사용될 수 있다. 예를 들어, 정수 값을 저장하는 데 보통 2바이트(16비트)가 필요하지만, 실제로 필요한 값은 1~2비트인 경우가 있다. 이러한 작은 변수들을 비트 필드로 묶으면 메모리 사용을 최적화할 수 있다.[5]
C에서는 `int`, `unsigned int`, `signed int` 등의 타입을 사용하여 비트 필드를 만들 수 있다. C++에서는 모든 정수 또는 열거형 타입을 사용할 수 있다. 프로그래머는 비트 필드 구조체를 선언하여 각 필드의 너비를 지정할 수 있다.[6]
비트 필드를 지원하지 않는 언어나, 프로그래머가 비트 표현을 직접 제어하고 싶은 경우에는 더 큰 워드 타입 내에서 비트를 수동으로 조작할 수 있다. 마스킹과 비트 연산을 조합하여 필드의 비트를 설정, 테스트, 변경할 수 있다.[7]
4. 1. 임베디드 시스템
비트 필드는 프로그램에서 여러 정수 변수가 필요할 때 메모리 소비를 줄이는 데 사용될 수 있다. 특히 메모리와 처리 능력이 제한된 임베디드 시스템에서 유용하다. 예를 들어, 많은 시스템에서 정수 값을 저장하려면 2바이트(16비트)의 메모리가 필요하지만, 실제로 저장할 값은 1~2비트만 필요한 경우가 있다. 이러한 작은 변수들을 비트 필드를 공유하도록 하면 메모리에서 데이터를 효율적으로 묶을 수 있다.[5]C에서는 `int`, `unsigned int`, `signed int`, `_Bool` (C99), `_BitInt(N)`, `unsigned _BitInt(N)` (C23) 또는 기타 구현 정의된 타입을 사용하여 비트 필드를 만들 수 있다. C++에서는 모든 정수 또는 열거형 타입을 사용하여 만들 수 있으며, 대부분의 C 컴파일러도 이를 허용한다. 프로그래머는 여러 하위 필드의 너비를 레이블링하고 결정하는 비트 필드에 대한 구조체를 선언할 수 있다.[6] 동일한 유형의 인접하게 선언된 비트 필드는 각 '필드'가 개별적으로 선언된 경우 사용되는 메모리와 비교하여 컴파일러에 의해 더 적은 수의 워드로 묶일 수 있다.
네이티브 비트 필드가 없는 언어나 프로그래머가 결과 비트 표현을 제어하려는 경우 더 큰 워드 타입 내에서 비트를 수동으로 조작할 수 있다. 이 경우 프로그래머는 마스킹 및 비트 연산의 조합을 사용하여 필드의 비트를 설정, 테스트 및 변경할 수 있다.[7]
4. 2. 운영체제
운영체제에서 프로세스 상태, 메모리 관리, 하드웨어 제어 등 다양한 분야에서 비트 필드가 활용된다. 많은 시스템에서 정수 값을 저장하려면 2바이트(16비트)의 메모리가 필요하지만, 실제로 저장할 값은 1~2비트만 필요한 경우가 있다. 이러한 작은 변수들을 비트 필드를 공유하도록 하면 메모리에서 데이터를 효율적으로 사용할 수 있다.[5]C에서는 `int`, `unsigned int`, `signed int`, `_Bool` (C99), `_BitInt(N)`, `unsigned _BitInt(N)` (C23) 또는 기타 구현 정의된 타입을 사용하여 비트 필드를 만들 수 있다. C++에서는 모든 정수 또는 열거형 타입을 사용하여 만들 수 있으며, 대부분의 C 컴파일러도 이를 허용한다. 프로그래머는 여러 하위 필드의 너비를 레이블링하고 결정하는 비트 필드에 대한 구조체를 선언할 수 있다.[6] 동일한 유형의 인접하게 선언된 비트 필드는 각 '필드'가 개별적으로 선언된 경우 사용되는 메모리와 비교하여 컴파일러에 의해 더 적은 수의 워드로 압축될 수 있다.
비트 필드가 없는 언어나 프로그래머가 결과 비트 표현을 제어하려는 경우 더 큰 워드 타입 내에서 비트를 수동으로 조작할 수 있다. 이 경우 프로그래머는 마스킹 및 비트 연산의 조합을 사용하여 필드의 비트를 설정, 테스트 및 변경할 수 있다.[7]
5. 추가 정보
비트 필드는 프로그램에서 여러 정수 변수가 필요할 때 메모리 소비를 줄이는 데 사용될 수 있다. 예를 들어, 많은 시스템에서 정수 값을 저장하려면 2바이트(16비트)의 메모리가 필요하지만, 실제로 저장할 값은 1~2비트만 필요할 수 있다. 이러한 작은 변수들을 비트 필드를 공유하도록 하면 메모리에서 데이터를 효율적으로 관리할 수 있다.[5]
C에서는 `int`, `unsigned int`, `signed int`, `_Bool` (C99), `_BitInt(N)`, `unsigned _BitInt(N)` (C23) 또는 기타 구현 정의된 타입을 사용하여 네이티브 구현 정의 비트 필드를 만들 수 있다. C++에서는 모든 정수 또는 열거형 타입을 사용하여 만들 수 있으며, 대부분의 C 컴파일러도 이를 허용한다. 프로그래머는 여러 하위 필드의 너비를 레이블링하고 결정하는 비트 필드에 대한 구조체를 선언할 수 있다.[6] 동일한 유형의 인접하게 선언된 비트 필드는 각 '필드'가 개별적으로 선언된 경우 사용되는 메모리와 비교하여 컴파일러에 의해 더 적은 수의 워드로 압축될 수 있다.
네이티브 비트 필드가 없는 언어나 프로그래머가 결과 비트 표현을 제어하려는 경우 더 큰 워드 타입 내에서 비트를 수동으로 조작할 수 있다. 이 경우 프로그래머는 마스킹 및 비트 연산의 조합을 사용하여 필드의 비트를 설정, 테스트 및 변경할 수 있다.[7]
5. 1. 스레드 안전성
비트 필드는 멀티스레드 환경에서 주의해서 사용해야 한다. 대부분의 CPU는 메모리 상의 임의의 비트를 직접 처리할 수 없고, 최소한 바이트 단위로만 접근할 수 있기 때문이다.[12]다음은 C 언어 코드 예시이다. 이 코드는 뮤텍스를 사용하더라도 스레드 안전하지 않다.
```c
struct foo {
int flag : 1;
int counter : 15;
};
struct foo my_foo;
pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
/* 한 스레드에서 실행되는 함수 */
void func1(void) {
pthread_mutex_lock(&my_mutex);
my_foo.flag = !my_foo.flag;
pthread_mutex_unlock(&my_mutex);
}
/* 다른 스레드에서 실행되는 함수 */
void func2(void) {
my_foo.counter += 1;
}
```
대부분의 CPU에서 `flag`와 `counter`를 별도로 로드하고 저장하는 것은 하드웨어 레벨에서 불가능[12]하기 때문에 `func1`과 `func2`가 동시에 실행되면 `my_foo.counter`의 값이 변경될 수 있다. 이러한 문제를 방지하려면, `counter` 자체가 스레드 안전할 필요가 없더라도, "flag와 counter 양쪽"에 대해 모든 접근 전후에 뮤텍스를 잠그고 해제해야 한다.[12]
참조
[1]
서적
80386 Assembly Language: A Complete Tutorial and Subroutine Library
https://books.google[...]
McGraw-Hill School Education Group
1988-08
[2]
서적
Practical C Programming
https://archive.org/[...]
"O'Reilly Media, Inc."
[3]
서적
The 68000 Microprocessor Family: Architecture, Programming, and Applications
https://books.google[...]
Merrill
1992-01
[4]
서적
Programming C# 4.0: Building Windows, Web, and RIA Applications for the .NET 4.0 Framework
https://books.google[...]
"O'Reilly Media, Inc."
2010-07-30
[5]
서적
Programming and Designing with the 68000 Family: Including 68000, 68010/12, 68020, and the 68030
https://books.google[...]
Prentice Hall
[6]
서적
C primer plus
Sams
[7]
서적
Expert JavaScript
https://books.google[...]
Apress
2013-11-13
[8]
서적
InCider
https://books.google[...]
W. Green
1986-01
[9]
웹사이트
INT13-C. ビット単位の演算子は符号無しオペランドに対してのみ使用する
https://www.jpcert.o[...]
[10]
웹사이트
Integer literal - cppreference.com
https://en.cpprefere[...]
[11]
웹사이트
Integer constant - cppreference.com
https://en.cpprefere[...]
[12]
웹사이트
C++ Bit Fields | Microsoft Docs
https://docs.microso[...]
[13]
서적
80386 Assembly Language: A Complete Tutorial and Subroutine Library
https://books.google[...]
McGraw-Hill School Education Group
1988-08
[14]
서적
Practical C Programming
https://books.google[...]
"O'Reilly Media, Inc."
[15]
서적
The 68000 Microprocessor Family: Architecture, Programming, and Applications
https://books.google[...]
Merrill
1992-01
[16]
서적
Programming C# 4.0: Building Windows, Web, and RIA Applications for the .NET 4.0 Framework
https://books.google[...]
"O'Reilly Media, Inc."
2010-07-30
[17]
서적
Data Structures
https://books.google[...]
PediaPress
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com