Jak odczytywać / zapisywać dowolne bity W C / C++

Zakładając, że mam bajt b o wartości binarnej 11111111

Jak na przykład odczytać 3-bitową wartość całkowitą rozpoczynającą się od drugiego bitu lub zapisać 4-bitową wartość całkowitą zaczynającą się od piątego bitu?

Author: dtech, 2012-08-05

8 answers

Jakieś 2+ lata po tym, jak zadałem to pytanie, chciałbym wyjaśnić je tak, jak chciałbym, aby zostało wyjaśnione, gdy byłem jeszcze kompletnym nowicjuszem i byłoby najbardziej korzystne dla ludzi, którzy chcą zrozumieć proces.

Po pierwsze, zapomnij o przykładowej wartości "11111111", która nie jest tak naprawdę odpowiednia do wizualnego wyjaśnienia procesu. Niech więc wartość początkowa będzie 10111011 (187 dziesiętnych), co będzie nieco bardziej ilustrujące proces.

1 - Jak czytać wartość 3-bitowa zaczynająca się od drugiego bitu:

    ___  <- those 3 bits
10111011 

Wartość jest 101, lub 5 w dziesiętnym, są 2 możliwe sposoby, aby ją uzyskać:

  • maska i przesunięcie

W tym podejściu potrzebne bity są najpierw maskowane wartością 00001110 (14 dziesiętnych), po czym są przesuwane w miejsce:

    ___
10111011 AND
00001110 =
00001010 >> 1 =
     ___
00000101

Wyrażenie to brzmiałoby: (value & 14) >> 1

  • shift i maska

To podejście jest podobne, ale kolejność operacji jest odwrócona, oznacza to, że oryginalna wartość jest przesunięta, a następnie zamaskowana za pomocą 00000111 (7), aby pozostawić tylko 3 ostatnie bity:

    ___
10111011 >> 1
     ___
01011101 AND
00000111
00000101

Wyrażenie to brzmiałoby: (value >> 1) & 7

Oba podejścia wiążą się z taką samą złożonością, a zatem nie różnią się wydajnością.

2 - Jak napisać wartość 3 bitową zaczynającą się od drugiego bitu:

W tym przypadku wartość początkowa jest znana, a jeśli tak jest w kodzie, możesz wymyślić sposób, aby ustawić znana wartość do innej znanej wartości, która używa mniej operacji, ale w rzeczywistości jest tak rzadko, przez większość czasu kod nie będzie znał ani wartości początkowej, ani tej, która ma być zapisana.

Oznacza to, że aby nowa wartość została pomyślnie "splifikowana" na bajt, bity docelowe muszą być ustawione na zero, po czym przesunięta wartość jest "splifikowana"w miejscu, co jest pierwszym krokiem:
    ___ 
10111011 AND
11110001 (241) =
10110001 (masked original value)

Drugim krokiem jest przesunięcie wartości, którą chcemy zapisać w 3 bity, powiedzmy, że chcemy zmienić to z 101 (5) na 110 (6)

     ___
00000110 << 1 =
    ___
00001100 (shifted "splice" value)

Trzecim i ostatnim krokiem jest połączenie zamaskowanej pierwotnej wartości z przesuniętą wartością "splice":

10110001 OR
00001100 =
    ___
10111101

Wyrażenie dla całego procesu będzie brzmiało: (value & 241) | (6 << 1)

Bonus - jak wygenerować maski odczytu i zapisu:

Oczywiście używanie konwertera binarnego na dziesiętny jest dalekie od elegancji, szczególnie w przypadku kontenerów 32 i 64-bitowych - wartości dziesiętne stają się szalenie Duże. Możliwe jest łatwo generować maski za pomocą wyrażeń, które kompilator może skutecznie rozwiązywać podczas kompilacji:

    W ten sposób można odczytać maskę dla "mask and shift": ((1 << fieldLength) - 1) << (fieldIndex - 1), zakładając, że indeks w pierwszym bitie wynosi 1 (nie zero)
  • Czytaj maskę dla "shift i maska": (1 << fieldLength) - 1 (Indeks nie odgrywa tu roli, ponieważ zawsze jest przesuwany do pierwszego bitu
  • zapis maski: po prostu odwróć wyrażenie maski "maska i przesunięcie" za pomocą operatora ~

Jak to działa (z 3-bitowe pole zaczynające się od drugiego bitu z powyższych przykładów)?

00000001 << 3
00001000  - 1
00000111 << 1
00001110  ~ (read mask)
11110001    (write mask)

Te same przykłady dotyczą szerszych liczb całkowitych oraz dowolnej szerokości i pozycji bitów pól, przy czym wartości przesunięcia i maski są odpowiednio różne.

Zauważ również, że przykłady zakładają unsigned integer, który jest tym, czego chcesz użyć, aby użyć liczb całkowitych jako przenośnej alternatywy pola bitowego( zwykłe pola bitowe nie są w żaden sposób gwarantowane przez standard jako przenośne), zarówno w lewo, jak iw prawo shift insert a padding 0, co nie ma miejsca w przypadku prawego przesunięcia podpisanej liczby całkowitej.

Jeszcze łatwiej:

W C++, ponieważ polega na generowaniu funkcji składowych, używa się tego zestawu makr: {[22]]}
#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); }

Możesz wybrać coś tak prostego jak:

struct A {
  uint bitData;
  FIELD(bitData, one, 0, 1)
  FIELD(bitData, two, 1, 2)
};

I mieć zaimplementowane pola bitowe jako właściwości, do których można łatwo uzyskać dostęp:

A a;
a.set_two(3);
cout << a.two();

Zastąp decltype przez GCC typeof pre-C++11.

 128
Author: dtech,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-02-03 07:43:11

Musisz przesunąć i zamaskować wartość, więc na przykład...

Jeśli chcesz przeczytać dwa pierwsze bity, musisz je zamaskować w ten sposób:

int value = input & 0x3;

Jeśli chcesz to przesunąć, musisz przesunąć w prawo N bitów, a następnie zamaskować bity, które chcesz:

int value = (intput >> 1) & 0x3;
Aby przeczytać trzy bity, jak zadałeś w swoim pytaniu.
int value = (input >> 1) & 0x7;
 16
Author: Geoffrey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-05 11:11:44

Po prostu użyj tego i feelfree:

#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     **/

Na przykład:

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
 9
Author: Hamid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-07 19:24:29

Musisz wykonać operację shift i maska (i). Niech b będzie dowolnym bajtem, a p będzie indeksem (>= 0) bitu, z którego chcesz pobrać N bity (>= 1).

Najpierw przesuń w prawo b o P razy:

x = b >> p;

Po drugie musisz zamaskować wynik za pomocą n jedynek:

mask = (1 << n) - 1;
y = x & mask;

Możesz umieścić wszystko w makrze:

#define TAKE_N_BITS_FROM(b, p, n) ((b) >> (p)) & ((1 << (n)) - 1)
 6
Author: Claudix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-05 11:41:06

" Jak na przykład odczytać 3-bitową wartość całkowitą zaczynającą się od drugiego bitu?"

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;

(założyłem, że" drugi bit " to bit # 2, czyli tak naprawdę trzeci bit.)

 3
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-05 11:11:24

Aby odczytać bajty użyj STD:: bitset

const int bits_in_byte = 8;

char myChar = 's';
cout << bitset<sizeof(myChar) * bits_in_byte>(myChar);

Aby pisać, musisz użyć operatorów bitowych, takich jak&^|&>. upewnij się, aby dowiedzieć się, co robią.

Na przykład, aby mieć 00100100, musisz ustawić pierwszy bit na 1 i przesunąć go z operatorami > 5 razy. jeśli chcesz kontynuować pisanie, po prostu ustaw pierwszy bit i przesuń go. to bardzo podobne do starej maszyny do pisania: piszesz i przesuwasz papier.

Dla 00100100: ustaw pierwszy bit na 1, Przesuń 5 razy, ustaw pierwszy bit na 1 i przesuń 2 razy:

const int bits_in_byte = 8;

char myChar = 0;
myChar = myChar | (0x1 << 5 | 0x1 << 2);
cout << bitset<sizeof(myChar) * bits_in_byte>(myChar);
 2
Author: MasterMastic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-05 11:34:36
int x = 0xFF;   //your number - 11111111

Jak na przykład odczytać 3-bitową wartość całkowitą zaczynającą się od drugiego bitu

int y = x & ( 0x7 << 2 ) // 0x7 is 111
                         // and you shift it 2 to the left
 1
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-05 11:10:34

Jeśli wciąż pobierasz bity ze swoich danych, możesz użyć pola bitowego. Musisz tylko skonfigurować strukturę i załadować ją tylko jedynkami i zerami:

struct bitfield{
    unsigned int bit : 1
}
struct bitfield *bitstream;

Następnie załaduj go w ten sposób (zamieniając char na int lub jakiekolwiek dane, które ładujesz):

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;
        }
    }
}

Następnie elementy dostępu:

bitstream[bitpointer].bit=...

Lub

...=bitstream[bitpointer].bit

Wszystko to zakłada, że działają na i86 / 64, a nie na arm, ponieważ arm może być duży lub mały endian.

 1
Author: Dominic Mason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-19 14:42:56