C/C++에서 임의의 비트를 읽고 쓰는 방법
바이너리 값이 111111인 바이트 b가 있다고 가정합니다.
예를 들어 두 번째 비트에서 시작하는 3비트 정수 값을 읽거나 다섯 번째 비트에서 시작하는 4비트 정수 값을 쓰려면 어떻게 해야 합니까?
제가 이 질문을 한 지 약 2년 이상 지난 지금, 저는 제가 아직 완전히 초보였을 때, 그리고 그 과정을 이해하고자 하는 사람들에게 가장 도움이 되는 방식으로 설명하고 싶습니다.
우선 프로세스의 시각적 설명에 적합하지 않은 "111111" 예제 값은 무시하십시오.초기값은 다음과 같습니다.10111011
(10진수 이하)는 프로세스의 설명을 조금 더 나타냅니다.
1 - 두 번째 비트부터 시작하는 3비트 값을 읽는 방법:
___ <- those 3 bits
10111011
값은 101 또는 10진수로는5 입니다.이 값을 취득하는 방법에는 다음 2가지가 있습니다.
- 마스크 앤 시프트
이 접근법에서는 먼저 필요한 비트가 값으로 마스크됩니다.00001110
(소수점 14) 이후 이동:
___
10111011 AND
00001110 =
00001010 >> 1 =
___
00000101
이를 나타내는 표현은 다음과 같습니다.(value & 14) >> 1
- 시프트와 마스크
이 접근법은 비슷하지만 조작 순서는 반대로 되어 있습니다.즉, 원래 값이 시프트된 후 다음 다음 값으로 마스킹됩니다.00000111
(7) 마지막 3비트만 남겨두려면:
___
10111011 >> 1
___
01011101 AND
00000111
00000101
이를 나타내는 표현은 다음과 같습니다.(value >> 1) & 7
두 방법 모두 복잡성이 동일하기 때문에 성능에는 차이가 없습니다.
2 - 두 번째 비트부터 시작하는 3비트 값을 쓰는 방법:
이 경우 초기값이 이미 알려져 있으며, 이것이 코드의 경우라면 기존의 값을 보다 적은 연산을 사용하는 다른 기존 값으로 설정할 수 있습니다.그러나 실제로는 거의 그렇지 않습니다.대부분의 경우 코드는 초기값이나 기입해야 할 값을 인식하지 않습니다.
즉, 새 값을 바이트로 "스플라이스"하려면 타깃 비트를 0으로 설정해야 합니다.그 후 첫 번째 단계인 시프트된 값을 "스플라이스"해야 합니다.
___
10111011 AND
11110001 (241) =
10110001 (masked original value)
두 번째 단계는 쓰려는 값을 3비트로 전환하는 것입니다. 예를 들어 값을 101(5)에서 110(6)로 변경합니다.
___
00000110 << 1 =
___
00001100 (shifted "splice" value)
세 번째이자 마지막 단계는 마스킹된 원래 값을 이동된 "스플라이스" 값과 스플라이스하는 것입니다.
10110001 OR
00001100 =
___
10111101
프로세스 전체의 표현은 다음과 같습니다.(value & 241) | (6 << 1)
보너스 - 읽기 및 쓰기 마스크를 생성하는 방법:
당연히 2진수에서 10진수로의 변환기를 사용하는 것은 우아한 것과는 거리가 멀며, 특히 32비트 및 64비트 컨테이너의 경우 10진수 값이 엄청나게 커집니다.컴파일러가 컴파일 중에 효율적으로 해결할 수 있는 표현식을 사용하여 마스크를 쉽게 생성할 수 있습니다.
- 마스크 및 시프트에 대한 판독 마스크:
((1 << fieldLength) - 1) << (fieldIndex - 1)
첫 번째 비트의 인덱스가 1(제로가 아님)이라고 가정합니다. - "shift and mask"에 대한 판독 마스크:
(1 << fieldLength) - 1
(여기에서는 인덱스는 항상 첫 번째 비트로 이동하기 때문에 인덱스는 역할을 하지 않습니다. - write mask : "mask and shift" 마스크 식을 반전합니다.
~
교환입니다.
어떻게 동작합니까(위의 예에서 두 번째 비트로 시작하는 3비트 필드)?
00000001 << 3
00001000 - 1
00000111 << 1
00001110 ~ (read mask)
11110001 (write mask)
더 넓은 정수, 임의 비트 폭 및 필드의 위치에도 동일한 예가 적용되며 이에 따라 시프트 및 마스크 값이 달라집니다.
또, 이 예에서는, 부호 없는 정수를 사용하는 것을 전제로 하고 있습니다.이것은, 포터블 비트 필드의 대체 수단으로서 정수를 사용하기 위해서입니다(통상적인 비트필드는 표준으로 포터블이 보증되지 않습니다).좌측과 우측 모두 패딩0 을 삽입합니다.이것은, 부호 있는 정수의 경우는 다릅니다.
한층 더 간단하게:
이 매크로 세트를 사용하는 경우(멤버 함수의 생성에 의존하므로 C++에서만 사용):
#define GETMASK(index, size) ((((size_t)1 << (size)) - 1) << (index))
#define READFROM(data, index, size) (((data) & GETMASK((index), (size))) >> (index))
#define WRITETO(data, index, size, value) ((data) = (((data) & (~GETMASK((index), (size)))) | (((value) << (index)) & (GETMASK((index), (size))))))
#define FIELD(data, name, index, size) \
inline decltype(data) name() const { return READFROM(data, index, size); } \
inline void set_##name(decltype(data) value) { WRITETO(data, index, size, value); }
다음과 같은 간단한 방법을 선택할 수 있습니다.
struct A {
uint bitData;
FIELD(bitData, one, 0, 1)
FIELD(bitData, two, 1, 2)
};
또한 쉽게 액세스할 수 있는 속성으로 비트 필드를 구현하십시오.
A a;
a.set_two(3);
cout << a.two();
교체하다decltype
gcc와 함께typeof
C++11 이전 버전
값을 이동 및 마스킹해야 합니다. 예를 들어...
처음 두 비트를 읽으려면 다음과 같이 마스킹하면 됩니다.
int value = input & 0x3;
오프셋하려면 N비트를 오른쪽으로 이동한 다음 원하는 비트를 마스크해야 합니다.
int value = (intput >> 1) & 0x3;
당신이 질문한 것처럼 세 부분을 읽는 것.
int value = (input >> 1) & 0x7;
이것만 사용하시면 됩니다.
#define BitVal(data,y) ( (data>>y) & 1) /** Return Data.Y value **/
#define SetBit(data,y) data |= (1 << y) /** Set Data.Y to 1 **/
#define ClearBit(data,y) data &= ~(1 << y) /** Clear Data.Y to 0 **/
#define TogleBit(data,y) (data ^=BitVal(y)) /** Togle Data.Y value **/
#define Togle(data) (data =~data ) /** Togle Data value **/
예를 들어 다음과 같습니다.
uint8_t number = 0x05; //0b00000101
uint8_t bit_2 = BitVal(number,2); // bit_2 = 1
uint8_t bit_1 = BitVal(number,1); // bit_1 = 0
SetBit(number,1); // number = 0x07 => 0b00000111
ClearBit(number,2); // number =0x03 => 0b0000011
시프트 앤 마스크(AND) 작업을 수행해야 합니다.b를 임의의 바이트로 하고 p를 n비트(>= 1)를 취할 비트의 인덱스(>= 0)로 합니다.
먼저 오른쪽 b에 p배만큼 이동해야 합니다.
x = b >> p;
다음으로 n개의 1로 결과를 마스킹해야 합니다.
mask = (1 << n) - 1;
y = x & mask;
모든 것을 매크로에 넣을 수 있습니다.
#define TAKE_N_BITS_FROM(b, p, n) ((b) >> (p)) & ((1 << (n)) - 1)
데이터로부터 비트를 계속 취득하는 경우는, 비트 필드를 사용할 필요가 있습니다.구조체를 설정하고 1과 0만 로딩하면 됩니다.
struct bitfield{
unsigned int bit : 1
}
struct bitfield *bitstream;
나중에 다음과 같이 로드합니다(int 또는 로드하는 데이터가 포함된 문자 표시).
long int i;
int j, k;
unsigned char c, d;
bitstream=malloc(sizeof(struct bitfield)*charstreamlength*sizeof(char));
for (i=0; i<charstreamlength; i++){
c=charstream[i];
for(j=0; j < sizeof(char)*8; j++){
d=c;
d=d>>(sizeof(char)*8-j-1);
d=d<<(sizeof(char)*8-1);
k=d;
if(k==0){
bitstream[sizeof(char)*8*i + j].bit=0;
}else{
bitstream[sizeof(char)*8*i + j].bit=1;
}
}
}
다음으로 액세스 요소:
bitstream[bitpointer].bit=...
또는
...=bitstream[bitpointer].bit
이 모든 것은 암이 아닌 i86/64에서 작동한다고 가정합니다. 암이 크거나 작을 수 있기 때문입니다.
"예를 들어 두 번째 비트에서 시작하는 3비트 정수 값을 읽으려면 어떻게 해야 합니까?"
int number = // whatever;
uint8_t val; // uint8_t is the smallest data type capable of holding 3 bits
val = (number & (1 << 2 | 1 << 3 | 1 << 4)) >> 2;
('second bit'는 2번 비트, 즉 3번 비트라고 가정했습니다.)
바이트를 읽으려면 std:: bitset을 사용합니다.
const int bits_in_byte = 8;
char myChar = 's';
cout << bitset<sizeof(myChar) * bits_in_byte>(myChar);
쓰려면 & ^ | & < > 등의 비트 단위 연산자를 사용해야 합니다.
예를 들어 00100100을 사용하려면 첫 번째 비트를 1로 설정하고 << >> 연산자와 5회 시프트해야 합니다.글을 계속 쓰고 싶으면 첫 번째 비트를 계속 맞추고 바꾸면 됩니다.오래된 타자기와 매우 흡사합니다. 글을 쓰고, 종이를 옮깁니다.
00100100의 경우: 첫 번째 비트를 1로 설정하고, 5회 이동하며, 첫 번째 비트를 1로 설정하고, 2회 이동시킵니다.
const int bits_in_byte = 8;
char myChar = 0;
myChar = myChar | (0x1 << 5 | 0x1 << 2);
cout << bitset<sizeof(myChar) * bits_in_byte>(myChar);
int x = 0xFF; //your number - 11111111
예를 들어 두 번째 비트에서 시작하는 3비트 정수 값을 읽는 방법은 무엇입니까?
int y = x & ( 0x7 << 2 ) // 0x7 is 111
// and you shift it 2 to the left
언급URL : https://stackoverflow.com/questions/11815894/how-to-read-write-arbitrary-bits-in-c-c
'IT이야기' 카테고리의 다른 글
구성 요소 페이지 Vue로 푸시할 때 재귀 발생 (0) | 2022.06.17 |
---|---|
vue-cli 앱의 node_modules에서 js 파일 가져오기 (0) | 2022.06.17 |
프록시를 사용하여 vue CLI 개발 서버에서 리다이렉트를 처리하려면 어떻게 해야 합니까? (0) | 2022.06.17 |
Vue.js 'v-bind:class'가 업데이트되지만 업데이트되지 않음 (0) | 2022.06.17 |
Java에서 2개의 목록에 가입하려면 어떻게 해야 하나요? (0) | 2022.06.17 |