3165 Stimmen

Wie kann man ein einzelnes Bit setzen, löschen und umschalten?

Wie kann man ein Bit setzen, löschen und umschalten?

90 Stimmen

Lesen Sie dies: graphics.stanford.edu/~seander/bithacks.html und, wenn du das gemeistert hast, lies das hier: realtimecollisiondetection.net/blog/?p=78

22 Stimmen

Das könnte Sie auch interessieren Der Gebissverdreher , Bit Twiddling Hacks und Die Aggregat-Magie-Algorithmen .

0 Stimmen

Das wirft die Frage auf, was die kanonische Frage für mehrere Bits ist.

58voto

yogeesh Punkte 1171

Von snip-c.zip 's bitops.h:

/*
**  Bit set, clear, and test operations
**
**  public domain snippet by Bob Stout
*/

typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

OK, lassen Sie uns die Dinge analysieren...

Der gemeinsame Ausdruck, mit dem Sie in all diesen Fällen Probleme zu haben scheinen, ist "(1L << (posn))". Damit wird lediglich eine Maske mit einem einzigen Bit auf und die mit jedem Integer-Typ funktionieren wird. Das Argument "posn" gibt die Position, an der Sie das Bit haben wollen. Wenn posn==0, dann wird dieser Ausdruck ausgewertet zu:

0000 0000 0000 0000 0000 0000 0000 0001 binary.

Wenn posn==8, wird es ausgewertet zu:

0000 0000 0000 0000 0000 0001 0000 0000 binary.

Mit anderen Worten: Es wird einfach ein Feld mit 0en und einer 1 an der angegebenen Position. Der einzige knifflige Teil befindet sich im Makro BitClr(), wo wir ein einzelnes 0-Bit in einem Feld mit 1en setzen müssen. Dies geschieht durch die Verwendung des 1er Komplements desselben Ausdrucks, der durch den Tilde-Operator (~) gekennzeichnet ist.

Sobald die Maske erstellt ist, wird sie auf das Argument angewendet, so wie Sie es vorschlagen, mit Hilfe der bitweisen Operatoren und (&), oder (|) und xor (^). Da die Maske vom Typ long ist, funktionieren die Makros genauso gut auf char's, short's, int's, oder longs.

Unterm Strich ist dies eine allgemeine Lösung für eine ganze Klasse von Probleme ist. Es ist natürlich möglich und sogar sinnvoll, die Makros mit expliziten Maskenwerten jedes Mal neu zu schreiben, wenn Sie brauchen, aber warum sollte man das tun? Denken Sie daran, dass die Makro-Substitution im Präprozessor, und so wird der generierte Code die Tatsache widerspiegeln, dass die Werte Werte vom Compiler als konstant angesehen werden - d.h. es ist genauso effizient, die die verallgemeinerten Makros zu verwenden, als jedes Mal das Rad neu zu erfinden, wenn man eine Bit-Manipulation.

Nicht überzeugt? Hier ist etwas Testcode - ich habe Watcom C mit voller Optimierung verwendet und ohne Verwendung von _cdecl, damit die resultierende Disassemblierung so sauber wie möglich ist möglich ist:

----[ TEST.C ]----------------------------------------------------------------

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

int bitmanip(int word)
{
      word = BitSet(word, 2);
      word = BitSet(word, 7);
      word = BitClr(word, 3);
      word = BitFlp(word, 9);
      return word;
}

----[ TEST.OUT (disassembliert) ]-----------------------------------------------

Module: C:\BINK\tst.c
Group: 'DGROUP' CONST,CONST2,_DATA,_BSS

Segment: _TEXT  BYTE   00000008 bytes  
 0000  0c 84             bitmanip_       or      al,84H    ; set bits 2 and 7
 0002  80 f4 02                          xor     ah,02H    ; flip bit 9 of EAX (bit 1 of AH)
 0005  24 f7                             and     al,0f7H
 0007  c3                                ret     

No disassembly errors

----[ finis ]-----------------------------------------------------------------

43voto

kapilddit Punkte 1675

Für den Anfänger möchte ich das anhand eines Beispiels etwas näher erläutern:

Exemple :

value is 0x55;
bitnum : 3rd.

Le site & Operator verwendet wird, prüft das Bit:

0101 0101
&
0000 1000
___________
0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)

Umschalten oder Umdrehen:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)

| Operator: Setzen des Bits

0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)

30voto

Hier ist mein Lieblingsmakro für Bit-Arithmetik, das für jede Art von vorzeichenlosem Integer-Array von unsigned char bis zu size_t (das ist der größte Typ, mit dem man effizient arbeiten sollte):

#define BITOP(a,b,op) \
 ((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))

Um ein Bit zu setzen:

BITOP(array, bit, |=);

Um es kurz zu machen:

BITOP(array, bit, &=~);

Um ein bisschen zu schalten:

BITOP(array, bit, ^=);

Um ein wenig zu testen:

if (BITOP(array, bit, &)) ...

usw.

30voto

John U Punkte 2739

Da dies als "embedded" gekennzeichnet ist, gehe ich davon aus, dass Sie einen Mikrocontroller verwenden. Alle oben genannten Vorschläge sind gültig und funktionieren (Lesen-Ändern-Schreiben, Unions, Structs, etc.).

Während einer Oszilloskop-basierten Fehlersuche war ich jedoch erstaunt, dass diese Methoden einen beträchtlichen Overhead an CPU-Zyklen haben, verglichen mit dem direkten Schreiben eines Wertes in die PORTnSET- / PORTnCLEAR-Register des Mikros, was einen echten Unterschied macht, wenn es enge Schleifen / hochfrequente ISRs gibt, die Pins umschalten.

Für diejenigen, die damit nicht vertraut sind: In meinem Beispiel hat der Mikrocontroller ein allgemeines Pin-Status-Register PORTn, das die Ausgangspins widerspiegelt, so dass PORTn |= BIT_TO_SET zu einem Lese-Änderungs-Schreibvorgang in diesem Register führt. Die PORTnSET / PORTnCLEAR-Register nehmen jedoch eine '1' an, um zu bedeuten "bitte dieses Bit auf 1 setzen" (SET) oder "bitte dieses Bit auf Null setzen" (CLEAR), und eine '0', um zu bedeuten "den Pin in Ruhe lassen". Also hat man am Ende zwei Port-Adressen, je nachdem, ob man das Bit setzt oder löscht (nicht immer praktisch), aber eine viel schnellere Reaktion und kleinerer assemblierter Code.

29voto

Pankaj Prakash Punkte 2096

Lassen Sie uns zunächst ein paar Dinge annehmen
num = 55 Ganzzahl zur Durchführung bitweiser Operationen (set, get, clear, toggle).
n = 4 0-basierte Bitposition, um bitweise Operationen durchzuführen.

Wie bekommt man ein Stück?

  1. Um die nth Bit der Ziffern Rechtsverschiebung num , n Zeiten. Dann führen Sie bitweise AND & mit 1.

    bit = (num >> n) & 1;

Wie funktioniert das?

       0011 0111 (55 in decimal)
    >>         4 (right shift 4 times)
-----------------
       0000 0011
     & 0000 0001 (1 in decimal)
-----------------
    => 0000 0001 (final result)

Wie setzt man ein Bit?

  1. Um ein bestimmtes Bit einer Zahl zu setzen. Linksverschiebung 1 n Zeiten. Führen Sie dann bitweise ODER | Betrieb mit num .

    num |= (1 << n); // Equivalent to; num = (1 << n) | num;

Wie funktioniert das?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     | 0011 0111 (55 in decimal)
-----------------
    => 0001 0000 (final result)

Wie kann man ein bisschen abräumen?

  1. Linksverschiebung 1, n Zeiten, d.h. 1 << n .
  2. Führen Sie das bitweise Komplement mit dem obigen Ergebnis durch. Damit wird das n-te Bit ungültig und der Rest der Bits wird gesetzt, d.h. ~ (1 << n) .
  3. Schließlich führen Sie bitweise AND aus & Operation mit dem obigen Ergebnis und num . Die drei oben genannten Schritte lassen sich wie folgt zusammenfassen num & (~ (1 << n)) ;

Steps to clear a bit

num &= (~(1 << n));    // Equivalent to; num = num & (~(1 << n));

Wie funktioniert das?

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
     ~ 0001 0000
-----------------
       1110 1111
     & 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

Wie schaltet man ein Bit um?

Um ein Bit umzuschalten, verwenden wir bitweises XOR ^ Betreiber. Der bitweise XOR-Operator ergibt 1, wenn die entsprechenden Bits der beiden Operanden unterschiedlich sind, andernfalls ergibt er 0.

Das heißt, um ein Bit umzuschalten, müssen wir eine XOR-Operation mit dem umzuschaltenden Bit und 1 durchführen.

num ^= (1 << n);    // Equivalent to; num = num ^ (1 << n);

Wie funktioniert das?

  • Wenn das umzuschaltende Bit 0 ist, dann, 0 ^ 1 => 1 .
  • Wenn das umzuschaltende Bit 1 ist, dann, 1 ^ 1 => 0 .

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)

       0001 0000
     ^ 0011 0111 (55 in decimal)

    => 0010 0111 (final result)

Empfohlene Lektüre - Übungen zum bitweisen Operator

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X