Frage Wie können Sie ein einzelnes Bit setzen, löschen und umschalten?


Wie können Sie ein Bit in C / C ++ setzen, löschen und umschalten?


2048
2017-09-07 00:42


Ursprung


Antworten:


Ein bisschen setzen

Verwenden Sie den bitweisen ODER-Operator (|) um ein bisschen zu setzen.

number |= 1UL << n;

Das wird die nDas bisschen number.

Benutzen 1ULL ob number ist breiter als unsigned long; Beförderung von 1UL << n passiert erst nach der Auswertung 1UL << n wo es undefiniert ist, das Verhalten um mehr als die Breite von a zu verschieben long. Das Gleiche gilt für alle anderen Beispiele.

Löschen ein bisschen

Verwenden Sie den bitweisen UND-Operator (&) um ein bisschen zu klären.

number &= ~(1UL << n);

Das wird klären nDas bisschen number. Sie müssen die Bitfolge mit dem bitweisen NOT-Operator invertieren (~), dann UND es.

Toggeln ein bisschen

Der XOR-Operator (^) kann verwendet werden, um ein bisschen zu wechseln.

number ^= 1UL << n;

Das schaltet die nDas bisschen number.

Ein bisschen prüfen

Du hast nicht darum gebeten, aber ich könnte es genauso gut hinzufügen.

Um ein bisschen zu prüfen, verschiebe die Zahl n nach rechts, dann bitweise UND:

bit = (number >> n) & 1U;

Das wird den Wert der nDas bisschen number in die Variable bit.

Wechseln ndas bisschen zu x

Einstellen der nth bisschen zu entweder 1 oder 0 kann mit dem folgenden auf einer Zweierkomplement-C ++ - Implementierung erreicht werden:

number ^= (-x ^ number) & (1UL << n);

Bit n Wird gesetzt wenn x ist 1und gelöscht, wenn x ist 0. Ob x hat einen anderen Wert, du bekommst Müll. x = !!x booleanize es auf 0 oder 1.

Um dies unabhängig von 2-Komplement-Negationsverhalten (wo -1 hat alle Bits gesetzt, im Gegensatz zu einer 1-Komplement oder Zeichen / Größe C ++ - Implementierung, verwenden Sie unsigned Negation.

number ^= (-(unsigned long)x ^ number) & (1UL << n);

oder

unsigned long newbit = !!x;    // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);

Es ist generell eine gute Idee, vorzeichenlose Typen für die portable Bit-Manipulation zu verwenden.

Es ist auch generell eine gute Idee, Code generell nicht zu kopieren und so viele Leute benutzen Präprozessor-Makros (wie das Community-Wiki antwortet weiter unten) oder eine Art Kapselung.


2997
2017-09-07 00:50



Verwenden der Standard-C ++ - Bibliothek: std::bitset<N>.

Oder der Boost Ausführung: boost::dynamic_bitset.

Es ist nicht nötig, selbst zu rollen:

#include <bitset>
#include <iostream>

int main()
{
    std::bitset<5> x;

    x[1] = 1;
    x[2] = 0;
    // Note x[0-4]  valid

    std::cout << x << std::endl;
}

[Alpha:] > ./a.out
00010

Die Boost-Version erlaubt ein Laufzeit-Bitset im Vergleich zu einem Standardbibliothek Kompilierzeit-Bitset.


381
2017-09-18 00:34



Die andere Option ist die Verwendung von Bitfeldern:

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

definiert ein 3-Bit-Feld (eigentlich sind es drei 1-Bit-Felder). Bitoperationen werden jetzt ein bisschen (haha) einfacher:

Um ein bisschen zu setzen oder zu löschen:

mybits.b = 1;
mybits.c = 0;

Um ein bisschen zu wechseln:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

Ein bisschen prüfen:

if (mybits.c)  //if mybits.c is non zero the next line below will execute

Dies funktioniert nur mit Bitfeldern fester Größe. Ansonsten müssen Sie auf die in früheren Beiträgen beschriebenen Bit-Twiddling-Techniken zurückgreifen.


212
2017-09-11 00:56



Ich benutze Makros, die in einer Header-Datei definiert sind, um Bit-Set und Clear zu behandeln:

/* a=target variable, b=bit number to act upon 0-n */
#define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
#define BIT_CHECK(a,b) ((a) & (1ULL<<(b)))

/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK_ALL(x,y) (((x) & (y)) == (y))   // warning: evaluates y twice
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))

125
2017-09-08 21:07



Es lohnt sich manchmal, ein enum zu Name die Bits:

enum ThingFlags = {
  ThingMask  = 0x0000,
  ThingFlag0 = 1 << 0,
  ThingFlag1 = 1 << 1,
  ThingError = 1 << 8,
}

Dann benutze die Namen später. I.e. schreiben

thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}

setzen, löschen und testen. Auf diese Weise verstecken Sie die magischen Zahlen vom Rest Ihres Codes.

Ansonsten unterstütze ich Jeremys Lösung.


99
2017-09-17 02:04



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, lass uns die Dinge analysieren ...

Der allgemeine Ausdruck, mit dem Sie Probleme zu haben scheinen, ist "(1L << (posn))". All dies macht eine Maske mit einem einzigen Bit an und das wird mit jedem Integer-Typ funktionieren. Das Argument "posn" gibt den Wert an Position, wo Sie das Bit wollen. Wenn posn == 0, dann wird dieser Ausdruck bewerten zu:

    0000 0000 0000 0000 0000 0000 0000 0001 binary.

Wenn posn == 8, wird es ausgewertet

    0000 0000 0000 0000 0000 0001 0000 0000 binary.

Mit anderen Worten, es erstellt einfach ein Feld von Nullen mit einer 1 an der angegebenen Stelle Position. Der einzige knifflige Teil ist das Makro BitClr (), das wir setzen müssen ein einzelnes 0-Bit in einem Feld von Einsen. Dies wird erreicht, indem die 1en verwendet werden Komplement des gleichen Ausdrucks wie durch den Operator Tilde (~) angegeben.

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

Die Quintessenz ist, dass dies eine allgemeine Lösung für eine ganze Klasse von ist Probleme. Es ist natürlich möglich und sogar angemessen, das neu zu schreiben Entsprechung von jedem dieser Makros mit expliziten Maskenwerten jedes Mal, wenn Sie brauche einen, aber warum? Denken Sie daran, dass die Makrosubstitution in der Präprozessor und so wird der generierte Code die Tatsache widerspiegeln, dass die Werte werden vom Compiler als konstant angesehen - d. h. es ist genauso effizient zu verwenden die verallgemeinerten Makros, um das Rad jedes Mal neu zu erfinden, wenn Sie es tun müssen Bit-Manipulation.

Nicht überzeugt? Hier ist ein Testcode - Ich habe Watcom C mit voller Optimierung verwendet und ohne _cdecl zu verwenden, wäre die resultierende Zerlegung so sauber wie möglich:

---- [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 (demontiert)] -------------------------------------- ---------

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] ------------------------------------------- ----------------------


34
2018-06-05 14:18



Für den Anfänger möchte ich ein bisschen mehr mit einem Beispiel erklären:

Beispiel:

value is 0x55;
bitnum : 3rd.

Das & Operator wird verwendet, überprüfen Sie 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)

Umkehren oder spiegeln:

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

| Operator: Setze das Bit

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

28
2017-09-07 00:45



Verwenden Sie die bitweisen Operatoren: &  | 

Um das letzte Bit zu setzen 000b:

foo = foo | 001b

Um das letzte Bit zu überprüfen foo:

if ( foo & 001b ) ....

Um das letzte Bit zu löschen foo:

foo = foo & 110b

ich benutzte XXXb zur Klarheit. Sie werden wahrscheinlich mit der HEX-Darstellung arbeiten, abhängig von der Datenstruktur, in die Sie Bits packen.


26
2017-07-13 06:53



Hier ist mein Lieblings-Bit-arithmetisches Makro, das für jede Art von vorzeichenlosen Integer-Arrays funktioniert unsigned char bis zu size_t (welches der größte Typ ist, 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 bisschen zu setzen:

BITOP(array, bit, |=);

Um ein bisschen zu klären:

BITOP(array, bit, &=~);

Um ein bisschen zu wechseln:

BITOP(array, bit, ^=);

Um ein bisschen zu testen:

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

etc.


24
2018-06-14 15:23



Da dies mit "embedded" gekennzeichnet ist, nehme ich an, dass Sie einen Mikrocontroller verwenden. Alle obigen Vorschläge sind gültig und funktionieren (read-modify-write, unions, structs usw.).

Bei einem Oszilloskop-basierten Debugging stellte ich jedoch mit Erstaunen fest, dass diese Methoden im Vergleich zu einem PORTnSET / PORTnCLEAR-Register des Mikros einen beträchtlichen Mehraufwand im Vergleich zum Schreiben eines Wertes haben, was einen echten Unterschied macht, wenn enge Schleifen / hoch sind der ISR-Umschaltknöpfe.

Für diejenigen, die nicht vertraut sind: In meinem Beispiel hat das Mikro ein allgemeines Pin-Status-Register PORTn, das die Ausgangs-Pins reflektiert, so dass dies PORTn | = BIT_TO_SET zu einem Read-Modify-Write zu diesem Register führt. Die PORTnSET / PORTnCLEAR-Register nehmen jedoch eine "1", um "Bitte machen Sie dieses Bit 1" (SET) oder "bitte machen dieses Bit Null" (CLEAR) und eine "0" zu bedeuten "Lassen Sie den Pin in Ruhe". Sie erhalten also zwei Portadressen, je nachdem, ob Sie das Bit setzen oder löschen (nicht immer praktisch), sondern a viel schnellere Reaktion und kleinerer zusammengesetzter Code.


22
2017-11-06 11:30