IT이야기

C/C++에서 임의의 비트를 읽고 쓰는 방법

cyworld 2022. 6. 17. 21:49
반응형

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();

교체하다decltypegcc와 함께typeofC++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

반응형