Wie setzen, löschen und schalten Sie ein einzelnes Bit um?

Antworten:

3599

Ein bisschen einstellen

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

number |= 1UL << n;

Das wird das nth Bit von setzen number. nsollte Null sein, wenn Sie das 1st-Bit setzen möchten und so weiter bis n-1, wenn Sie das nth-Bit setzen möchten .

Verwenden Sie, 1ULLwenn numberbreiter als ist unsigned long; Die Förderung von 1UL << nerfolgt erst nach der Bewertung, 1UL << nwo es undefiniertes Verhalten ist, um mehr als die Breite von a zu verschieben long. Gleiches gilt für alle übrigen Beispiele.

Ein bisschen klären

Verwenden Sie den bitweisen AND-Operator ( &), um ein Bit zu löschen.

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

Das wird das ndritte bisschen klären number. Sie müssen die Bitfolge mit dem bitweisen NOT-Operator ( ~) invertieren und dann AND it.

Ein bisschen umschalten

Mit dem XOR-Operator ( ^) können Sie ein wenig umschalten.

number ^= 1UL << n;

Das wird das nth Bit von umschalten number.

Ein bisschen nachsehen

Sie haben nicht danach gefragt, aber ich könnte es genauso gut hinzufügen.

Um ein Bit zu überprüfen, verschieben Sie die Zahl n nach rechts und dann bitweise UND es:

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

Dadurch wird der Wert des nth-Bits von numberin die Variable eingefügt bit.

Ändern des n- ten Bits in x

Das Setzen des nth-Bits auf entweder 1oder 0kann mit den folgenden Optionen für eine C ++ - Komplementimplementierung erreicht werden:

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

Das Bit nwird gesetzt, wenn es xist 1, und gelöscht, wenn es xist 0. Wenn xes einen anderen Wert gibt, erhalten Sie Müll. x = !!xwird es auf 0 oder 1 booleanisieren.

-1Verwenden Sie eine vorzeichenlose Negation, um dies unabhängig vom 2er-Komplement-Negationsverhalten zu machen (bei dem alle Bits gesetzt sind, im Gegensatz zu einer 1-Komplement- oder Vorzeichen- / Größen-C ++ - Implementierung).

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

oder

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

Im Allgemeinen ist es eine gute Idee, vorzeichenlose Typen für die Manipulation von tragbaren Bits zu verwenden.

oder

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

(number & ~(1UL << n))löscht das nth-Bit und (x << n)setzt das nth-Bit auf x.

Es ist im Allgemeinen auch eine gute Idee, Code im Allgemeinen nicht zu kopieren / einzufügen, und so viele Leute verwenden Präprozessor-Makros (wie die Antwort des Community-Wikis weiter unten ) oder eine Art Kapselung.

Jeremy Ruten
quelle
128
Ich möchte darauf hinweisen, dass Compiler auf Plattformen, die native Unterstützung für das Setzen / Löschen von Bits bieten (z. B. AVR-Mikrocontroller), häufig 'myByte | = (1 << x)' in die nativen Anweisungen zum Setzen / Löschen von Bits übersetzen, wann immer x ist eine Konstante, zB: (1 << 5) oder const ohne Vorzeichen x = 5.
Aaron
52
Bit = Zahl & (1 << x); setzt den Wert von Bit x nicht in Bit, es sei denn, Bit hat den Typ _Bool (<stdbool.h>). Ansonsten ist bit = !! (Zahl & (1 << x)); wird ..
Chris Young
23
Warum ändern Sie nicht den letzten inbit = (number >> x) & 1
Aaronon
42
1ist ein intLiteral, das signiert ist. Alle Operationen hier arbeiten also mit vorzeichenbehafteten Nummern, die in den Standards nicht genau definiert sind. Die Standards garantieren keine Zweierkomplementierung oder arithmetische Verschiebung, daher ist die Verwendung besser 1U.
Siyuan Ren
50
Ich bevorzuge das number = number & ~(1 << n) | (x << n);Ändern des n-ten Bits in x.
Jiasli
459

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

Oder die Boost- Version : boost::dynamic_bitset.

Sie müssen nicht selbst 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 ermöglicht ein Bit-Set in Laufzeitgröße im Vergleich zu einem Standard- Bit-Set in Kompilierungszeit für Bibliotheken .

Martin York
quelle
34
+1. Nicht dass std :: bitset von "C" aus verwendet werden kann, aber da der Autor seine Frage mit "C ++", AFAIK, markiert hat, ist Ihre Antwort hier die beste ... std :: vector <bool> ist ein anderer Weg, wenn man seine Vor- und Nachteile kennt
19.
23
@andrewdotnich: vector <bool> ist (leider) eine Spezialisierung, die die Werte als Bits speichert. Siehe gotw.ca/publications/mill09.htm für weitere Informationen ...
Niklas
71
Vielleicht hat es niemand erwähnt, weil dies als eingebettet markiert war. In den meisten eingebetteten Systemen vermeiden Sie STL wie die Pest. Und Boost-Unterstützung ist wahrscheinlich ein sehr seltener Vogel unter den meisten eingebetteten Compilern.
Lundin
17
@ Martin Es ist sehr wahr. Neben bestimmten Leistungskillern wie STL und Vorlagen vermeiden viele eingebettete Systeme sogar die gesamten Standardbibliotheken vollständig, da sie nur schwer zu überprüfen sind. Der größte Teil des Embedded-Zweigs umfasst Standards wie MISRA, die statische Code-Analyse-Tools erfordern (Software-Profis sollten solche Tools übrigens verwenden, nicht nur Embedded-Leute). Im Allgemeinen haben Benutzer bessere Möglichkeiten, als statische Analysen in der gesamten Standardbibliothek durchzuführen - wenn der Quellcode ihnen sogar auf dem jeweiligen Compiler zur Verfügung steht.
Lundin
37
@Lundin: Ihre Aussagen sind zu weit gefasst (daher nutzlos, um darüber zu streiten). Ich bin sicher, dass ich Situationen finden kann, in denen sie wahr sind. Dies ändert nichts an meinem Ausgangspunkt. Beide Klassen eignen sich perfekt für die Verwendung in eingebetteten Systemen (und ich weiß, dass sie verwendet werden). Ihr anfänglicher Punkt, dass STL / Boost auf eingebetteten Systemen nicht verwendet wird, ist ebenfalls falsch. Ich bin sicher, dass es Systeme gibt, die sie nicht verwenden, und selbst die Systeme, die sie verwenden, werden mit Bedacht verwendet, aber zu sagen, dass sie nicht verwendet werden, ist einfach nicht korrekt (weil es Systeme gibt, in denen sie verwendet werden).
Martin York
248

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 (tatsächlich sind es drei 1-Bit-Felder). Bitoperationen werden jetzt etwas (haha) einfacher:

So setzen oder löschen Sie ein bisschen:

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

Um ein bisschen umzuschalten:

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

Ein bisschen nachsehen:

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

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

Ferruccio
quelle
68
Ich habe immer festgestellt, dass die Verwendung von Bitfeldern eine schlechte Idee ist. Sie haben keine Kontrolle über die Reihenfolge, in der die Bits zugewiesen werden (von oben oder unten), sodass es unmöglich ist, den Wert auf stabile / tragbare Weise zu serialisieren, außer bitweise. Es ist auch unmöglich, DIY-Bitarithmetik mit Bitfeldern zu mischen, beispielsweise eine Maske zu erstellen, die mehrere Bits gleichzeitig testet. Sie können natürlich && verwenden und hoffen, dass der Compiler es richtig optimiert ...
R .. GitHub STOP HELPING ICE
34
Bitfelder sind in vielerlei Hinsicht schlecht, ich könnte fast ein Buch darüber schreiben. Tatsächlich musste ich das fast für ein kleines Feldprogramm tun, das MISRA-C-Konformität benötigte. MISRA-C erzwingt die Dokumentation des gesamten implementierungsdefinierten Verhaltens, sodass ich am Ende einen ziemlichen Aufsatz über alles geschrieben habe, was in Bitfeldern schief gehen kann. Bitreihenfolge, Endianess, Auffüllen von Bits, Auffüllen von Bytes, verschiedene andere Ausrichtungsprobleme, implizite und explizite Typkonvertierungen in und aus einem Bitfeld, UB, wenn int nicht verwendet wird und so weiter. Verwenden Sie stattdessen bitweise Operatoren für weniger Fehler und portablen Code. Bitfelder sind vollständig redundant.
Lundin
44
Wie die meisten Sprachfunktionen können Bitfelder korrekt verwendet oder missbraucht werden. Wenn Sie mehrere kleine Werte in ein einzelnes int packen müssen, können Bitfelder sehr nützlich sein. Wenn Sie jedoch anfangen, Annahmen darüber zu treffen, wie die Bitfelder dem tatsächlich enthaltenen int zugeordnet sind, fragen Sie nur nach Problemen.
Ferruccio
4
@endolith: Das wäre keine gute Idee. Sie könnten es zum Laufen bringen, aber es wäre nicht unbedingt für einen anderen Prozessor oder einen anderen Compiler oder sogar für die nächste Version desselben Compilers portierbar.
Ferruccio
3
@Yasky und Ferruccio, die unterschiedliche Antworten auf eine sizeof () für diesen Ansatz erhalten, sollten die Probleme mit der Kompatibilität nicht nur zwischen Compilern, sondern auch zwischen Hardware veranschaulichen. Wir täuschen uns manchmal vor, dass wir diese Probleme mit Sprachen oder definierten Laufzeiten gelöst haben, aber es kommt wirklich darauf an, ob es auf meinem Computer funktioniert. Ihr eingebetteten Jungs habt meinen Respekt (und mein Mitgefühl).
Kelly S. French
181

Ich verwende Makros, die in einer Header-Datei definiert sind, um das Setzen und Löschen von Bits zu handhaben:

/* 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))))        // '!!' to make sure this returns 0 or 1

/* 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))
Steve Karg
quelle
17
Äh, mir ist klar, dass dies ein 5 Jahre alter Beitrag ist, aber es gibt keine Argumentduplikation in einem dieser Makros, Dan
Robert Kelly
11
BITMASK_CHECK(x,y) ((x) & (y))muss sein, ((x) & (y)) == (y)sonst gibt es ein falsches Ergebnis auf der Multibit-Maske zurück (zB 5vs. 3) / * Hallo an alle Totengräber
:)
7
1sollte (uintmax_t)1oder ähnlich sein, falls jemand versucht, diese Makros auf einem longoder einem größeren Typ zu verwenden
MM
2
BITMASK_CHECK_ALL(x,y)kann implementiert werden als!~((~(y))|(x))
Handy999
3
@ Handy999 Es ist ein bisschen einfacher zu sehen, warum das funktioniert, nachdem De Morgans Gesetz !(~(x) & (y))
angewendet
114

Manchmal lohnt es sich, die Bits mit a enumzu benennen :

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

Verwenden Sie dann die Namen später. Dh schreibe

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

einstellen, löschen und testen. Auf diese Weise verstecken Sie die magischen Zahlen vor dem Rest Ihres Codes.

Davon abgesehen unterstütze ich Jeremys Lösung.

dmckee --- Ex-Moderator Kätzchen
quelle
1
Alternativ können Sie clearbits()stattdessen eine Funktion erstellen &= ~. Warum verwenden Sie dafür eine Aufzählung? Ich dachte, diese dienen dazu, eine Reihe eindeutiger Variablen mit einem versteckten beliebigen Wert zu erstellen, aber Sie weisen jedem einen bestimmten Wert zu. Was ist der Vorteil, wenn man sie nur als Variablen definiert?
Endolith
4
@endolith: Die Verwendung von enums für Mengen verwandter Konstanten reicht in der c-Programmierung weit zurück. Ich vermute, dass bei modernen Compilern der einzige Vorteil gegenüber const shortoder was auch immer darin besteht, dass sie explizit in Gruppen zusammengefasst sind. Und wenn Sie sie für etwas anderes als Bitmasken wollen, erhalten Sie die automatische Nummerierung. In c ++ bilden sie natürlich auch unterschiedliche Typen, was Ihnen einige zusätzliche statische Fehlerprüfungen bietet.
dmckee --- Ex-Moderator Kätzchen
Sie erhalten undefinierte Enum-Konstanten, wenn Sie nicht für jeden der möglichen Werte der Bits eine Konstante definieren. Wofür ist der enum ThingFlagsWert ThingError|ThingFlag1zum Beispiel?
Luis Colorado
6
Wenn Sie diese Methode verwenden, beachten Sie bitte, dass Enum-Konstanten immer vom vorzeichenbehafteten Typ sind int. Dies kann aufgrund impliziter Ganzzahl-Heraufstufung oder bitweiser Operationen für signierte Typen alle Arten von subtilen Fehlern verursachen. thingstate = ThingFlag1 >> 1ruft zum Beispiel ein implementierungsdefiniertes Verhalten auf. thingstate = (ThingFlag1 >> x) << ykann undefiniertes Verhalten aufrufen. Und so weiter. Um sicher zu gehen, verwenden Sie immer einen vorzeichenlosen Typ.
Lundin
1
@Lundin: Ab C ++ 11 können Sie den zugrunde liegenden Typ einer Aufzählung festlegen, z. B.: enum My16Bits: unsigned short { ... };
Aiken Drum
47

Aus der bitops.h von snip-c.zip:

/*
**  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 übliche Ausdruck, mit dem Sie in all diesen Fällen Probleme zu haben scheinen, ist "(1L << (posn))". Dazu wird lediglich eine Maske mit einem einzelnen Bit erstellt, die mit jedem Ganzzahltyp funktioniert. Das Argument "posn" gibt die Position an, an der Sie das Bit möchten. Wenn posn == 0 ist, wird dieser Ausdruck wie folgt ausgewertet:

0000 0000 0000 0000 0000 0000 0000 0001 binary.

Wenn posn == 8 ist, wird Folgendes ausgewertet:

0000 0000 0000 0000 0000 0001 0000 0000 binary.

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

Sobald die Maske erstellt wurde, wird sie mit den Operatoren bitweise und (&) oder (|) und xor (^) wie von Ihnen vorgeschlagen auf das Argument angewendet. Da die Maske vom Typ lang ist, funktionieren die Makros genauso gut für Zeichen, kurze, int oder lange.

Das Fazit ist, dass dies eine allgemeine Lösung für eine ganze Klasse von Problemen ist. Es ist natürlich möglich und sogar angemessen, das Äquivalent eines dieser Makros jedes Mal, wenn Sie eines benötigen, mit expliziten Maskenwerten neu zu schreiben, aber warum? Denken Sie daran, dass die Makrosubstitution im Präprozessor erfolgt und der generierte Code die Tatsache widerspiegelt, dass die Werte vom Compiler als konstant angesehen werden. Das heißt, es ist genauso effizient, die verallgemeinerten Makros zu verwenden, wie das Rad jedes Mal neu zu erfinden Bitmanipulation durchführen.

Nicht überzeugt? Hier ist ein Testcode: Ich habe Watcom C mit vollständiger Optimierung und ohne Verwendung von _cdecl verwendet, damit die resultierende Demontage so sauber wie 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 (zerlegt)] -------------------------------------- ---------

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

yogeesh
quelle
3
2 Dinge dazu: (1) Bei der Durchsicht Ihrer Makros glauben einige möglicherweise fälschlicherweise, dass die Makros tatsächlich Bits im Argument setzen / löschen / umdrehen, es gibt jedoch keine Zuordnung; (2) Ihr test.c ist nicht vollständig; Ich vermute, wenn Sie mehr Fälle ausführen würden, würden Sie ein Problem finden (Leserübung)
Dan
19
-1 Das ist nur seltsame Verschleierung. Erfinden Sie die C-Sprache niemals neu, indem Sie die Sprachsyntax hinter Makros verstecken. Dies ist eine sehr schlechte Praxis. Dann einige Kuriositäten: Zuerst wird 1L signiert, was bedeutet, dass alle Bitoperationen an einem signierten Typ ausgeführt werden. Alles, was an diese Makros übergeben wird, wird als lange signiert zurückgegeben. Nicht gut. Zweitens funktioniert dies auf kleineren CPUs sehr ineffizient, da es lange erzwingt, wenn die Operationen auf int-Ebene gewesen sein könnten. Drittens sind funktionsähnliche Makros die Wurzel allen Übels: Sie haben keinerlei Typensicherheit. Auch der vorherige Kommentar zu keiner Zuordnung ist sehr gültig.
Lundin
2
Dies wird fehlschlagen, wenn dies der Fall argist long long. 1Lmuss der breitestmögliche Typ sein, also (uintmax_t)1. (Sie könnten mit durchkommen 1ull)
MM
Haben Sie die Codegröße optimiert? Auf Intel-Mainstream-CPUs kommt es beim Lesen von AX oder EAX nach der Rückkehr dieser Funktion zu Teilregisterstillständen, da die 8-Bit-Komponenten von EAX geschrieben werden. (Es ist in Ordnung bei AMD-CPUs oder anderen, die Teilregister nicht getrennt vom vollständigen Register umbenennen . Haswell / Skylake benennen AL nicht separat um, aber sie benennen AH um. )
Peter Cordes
37

Verwenden Sie die bitweisen Operatoren: & |

So setzen Sie das letzte Bit 000b:

foo = foo | 001b

So checken Sie das letzte Bit ein foo:

if ( foo & 001b ) ....

So löschen Sie das letzte Bit in foo:

foo = foo & 110b

Ich habe XXXbaus Gründen der Klarheit verwendet. Abhängig von der Datenstruktur, in die Sie Bits packen, werden Sie wahrscheinlich mit der HEX-Darstellung arbeiten.

nsanders
quelle
7
In C gibt es keine binäre Notation. Binäre Ganzzahlkonstanten sind eine nicht standardmäßige Erweiterung.
Lundin
Verwenden Sie XOR, um ein wenig umzuschalten:foo = foo ^ MY_MASK
Peter L
Verwenden Sie NICHT, um eine Maske umzukehren, um klar zu machen:foo = foo & ~MY_MASK
Peter L
32

Für den Anfänger möchte ich etwas mehr anhand eines Beispiels erklären:

Beispiel:

value is 0x55;
bitnum : 3rd.

Der &Operator wird verwendet, um das Bit zu überprüfen:

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)

Toggle oder Flip:

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

| Operator: Setzen Sie das Bit

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

Hier ist mein Lieblingsbit-Arithmetik-Makro, das für alle Arten von vorzeichenlosen Ganzzahl-Arrays von unsigned charbis zu size_t(der größte Typ, mit dem effizient gearbeitet werden sollte) funktioniert:

#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 umzuschalten:

BITOP(array, bit, ^=);

Um ein bisschen zu testen:

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

usw.

R .. GitHub HÖREN SIE AUF, EIS ZU HELFEN
quelle
5
Es ist gut zu lesen, aber man sollte sich möglicher Nebenwirkungen bewusst sein. Die Verwendung BITOP(array, bit++, |=);in einer Schleife wird höchstwahrscheinlich nicht das tun, was der Anrufer will.
Foraidt
Tatsächlich. =) Eine Variante, die Sie vielleicht bevorzugen, besteht darin, sie in zwei Makros zu unterteilen, eines zum Adressieren des Array-Elements und das andere zum Verschieben des Bits, ala BITCELL(a,b) |= BITMASK(a,b);(beide nehmen aals Argument, um die Größe zu bestimmen, aber letztere würden aseitdem nie ausgewertet es erscheint nur in sizeof).
R .. GitHub STOP HELPING ICE
1
@R .. Diese Antwort ist wirklich alt, aber ich würde in diesem Fall wahrscheinlich eine Funktion einem Makro vorziehen.
PC Luddite
Minor: die dritte (size_t)Besetzung scheint es zu sein , nur einige , um sicherzustellen , ohne Vorzeichen Mathe mit %. Könnte (unsigned)da sein.
chux
Das (size_t)(b)/(8*sizeof *(a))könnte sich bvor der Teilung unnötig verengen . Nur ein Problem mit sehr großen Bit-Arrays. Immer noch ein interessantes Makro.
chux
25

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

Während eines Oszilloskop-basierten Debuggens stellte ich jedoch erstaunt fest, dass diese Methoden einen erheblichen Overhead in den CPU-Zyklen haben, verglichen mit dem Schreiben eines Werts direkt in die PORTnSET / PORTnCLEAR-Register des Mikros, was bei engen Schleifen / High einen echten Unterschied macht -Frequenz-ISR-Umschaltstifte.

Für Unbekannte: In meinem Beispiel hat das Mikro ein allgemeines Pin-State-Register PORTn, das die Ausgangspins widerspiegelt. Wenn Sie also PORTn | = BIT_TO_SET ausführen, wird in dieses Register gelesen, geändert und geschrieben. Die PORTnSET / PORTnCLEAR-Register haben jedoch eine '1', um "Bitte machen Sie dieses Bit 1" (SET) oder "Bitte machen Sie dieses Bit auf Null" (CLEAR) und eine '0', um "Lassen Sie den Pin in Ruhe" zu bedeuten. Sie haben also zwei Portadressen, je nachdem, ob Sie das Bit setzen oder löschen (nicht immer praktisch), aber eine viel schnellere Reaktion und kleineren zusammengesetzten Code.

John U.
quelle
Micro war Coldfire MCF52259 unter Verwendung von C in Codewarrior. Das Betrachten des Disassemblers / Asms ist eine nützliche Übung, da hier alle Schritte aufgeführt sind, die die CPU ausführen muss, um selbst die grundlegendste Operation auszuführen. <br> Wir haben auch andere CPU-Hogging-Anweisungen in zeitkritischen Schleifen entdeckt. Das Einschränken einer Variablen durch Ausführen von var% = max_val kostet jedes Mal einen Stapel von CPU-Zyklen, während dies der Fall ist, wenn (var> max_val) var- = max_val nur verwendet ein paar Anweisungen. <br> Eine gute Anleitung für ein paar weitere Tricks finden Sie hier: codeproject.com/Articles/6154/…
John U
Noch wichtiger ist, dass die E / A-Register mit Hilfsspeicherzuordnung einen Mechanismus für atomare Aktualisierungen bereitstellen. Lesen / Ändern / Schreiben kann sehr schlecht laufen, wenn die Sequenz unterbrochen wird.
Ben Voigt
1
Beachten Sie, dass alle Portregister als definiert sind volatileund der Compiler daher keine Optimierungen für Code vornehmen kann, an dem solche Register beteiligt sind. Daher ist es empfehlenswert, solchen Code zu zerlegen und zu sehen, wie er sich auf Assembler-Ebene herausstellte.
Lundin
24

Der Bitfeldansatz hat andere Vorteile in der eingebetteten Arena. Sie können eine Struktur definieren, die direkt auf die Bits in einem bestimmten Hardwareregister abgebildet wird.

struct HwRegister {
    unsigned int errorFlag:1;  // one-bit flag field
    unsigned int Mode:3;       // three-bit mode field
    unsigned int StatusCode:4;  // four-bit status code
};

struct HwRegister CR3342_AReg;

Sie müssen sich der Bit-Packreihenfolge bewusst sein - ich denke, es ist zuerst MSB, aber dies kann implementierungsabhängig sein. Überprüfen Sie außerdem, wie Ihre Compiler-Handler Felder überschreiten, die Bytegrenzen überschreiten.

Sie können dann die einzelnen Werte wie zuvor lesen, schreiben und testen.

Roddy
quelle
2
So ziemlich alles an Bitfeldern ist implementierungsdefiniert. Selbst wenn Sie alle Details darüber herausfinden, wie Ihr bestimmter Compiler sie implementiert, wird sie durch die Verwendung in Ihrem Code mit Sicherheit nicht portierbar.
Lundin
1
@Lundin - Richtig, aber eingebettetes System-Bit-Fiddling (insbesondere in Hardwareregistern, worauf sich meine Antwort bezieht) wird sowieso nie nützlich portabel sein.
Roddy
1
Vielleicht nicht zwischen ganz anderen CPUs. Sie möchten jedoch höchstwahrscheinlich, dass es zwischen Compilern und zwischen verschiedenen Projekten portierbar ist. Und es gibt eine Menge eingebetteter "Bit-Fiddling", die überhaupt nichts mit der Hardware zu tun haben, wie z. B. das Codieren / Decodieren von Datenprotokollen.
Lundin
... und wenn Sie es sich zur Gewohnheit machen, Bitfelder für die eingebettete Programmierung zu verwenden, wird Ihr X86-Code schneller und schlanker ausgeführt. Nicht in einfachen Benchmarks, in denen Sie die gesamte Maschine haben, um den Benchmark zu zerstören, sondern in realen Multitasking-Umgebungen, in denen Programme um Ressourcen konkurrieren. Vorteil CISC - dessen ursprüngliches Entwurfsziel darin bestand, CPUs schneller als Busse und langsamen Speicher auszugleichen.
20

Überprüfen Sie ein Bit an einer beliebigen Stelle in einer Variablen beliebigen Typs:

#define bit_test(x, y)  ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )

Beispielnutzung:

int main(void)
{
    unsigned char arr[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };

    for (int ix = 0; ix < 64; ++ix)
        printf("bit %d is %d\n", ix, bit_test(arr, ix));

    return 0;
}

Hinweise: Dies ist so konzipiert, dass es schnell (aufgrund seiner Flexibilität) und nicht verzweigt ist. Dies führt zu einem effizienten SPARC-Maschinencode, wenn Sun Studio 8 kompiliert wird. Ich habe es auch mit MSVC ++ 2008 auf amd64 getestet. Es ist möglich, ähnliche Makros zum Setzen und Löschen von Bits zu erstellen. Der Hauptunterschied dieser Lösung im Vergleich zu vielen anderen hier besteht darin, dass sie für jeden Ort in so ziemlich jeder Art von Variable funktioniert.

John Zwinck
quelle
20

Allgemeiner für Bitmaps beliebiger Größe:

#define BITS 8
#define BIT_SET(  p, n) (p[(n)/BITS] |=  (0x80>>((n)%BITS)))
#define BIT_CLEAR(p, n) (p[(n)/BITS] &= ~(0x80>>((n)%BITS)))
#define BIT_ISSET(p, n) (p[(n)/BITS] &   (0x80>>((n)%BITS)))
Rechnung
quelle
2
CHAR_BITist bereits definiert durch limits.h, müssen Sie nicht Ihre eigenen BITSeingeben (und in der Tat machen Sie Ihren Code dadurch schlechter)
MM
14

Dieses Programm soll jedes Datenbit von 0 auf 1 oder 1 auf 0 ändern:

{
    unsigned int data = 0x000000F0;
    int bitpos = 4;
    int bitvalue = 1;
    unsigned int bit = data;
    bit = (bit>>bitpos)&0x00000001;
    int invbitvalue = 0x00000001&(~bitvalue);
    printf("%x\n",bit);

    if (bitvalue == 0)
    {
        if (bit == 0)
            printf("%x\n", data);
        else
        {
             data = (data^(invbitvalue<<bitpos));
             printf("%x\n", data);
        }
    }
    else
    {
        if (bit == 1)
            printf("elseif %x\n", data);
        else
        {
            data = (data|(bitvalue<<bitpos));
            printf("else %x\n", data);
        }
    }
}
Gokul Naathan
quelle
14

Wenn Sie viel herumtollen, möchten Sie vielleicht Masken verwenden, die das Ganze schneller machen. Die folgenden Funktionen sind sehr schnell und dennoch flexibel (sie ermöglichen das Bit-Twiddling in Bitmaps jeder Größe).

const unsigned char TQuickByteMask[8] =
{
   0x01, 0x02, 0x04, 0x08,
   0x10, 0x20, 0x40, 0x80,
};


/** Set bit in any sized bit mask.
 *
 * @return    none
 *
 * @param     bit    - Bit number.
 * @param     bitmap - Pointer to bitmap.
 */
void TSetBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] |= TQuickByteMask[n];        // Set bit.
}


/** Reset bit in any sized mask.
 *
 * @return  None
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
void TResetBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] &= (~TQuickByteMask[n]);    // Reset bit.
}


/** Toggle bit in any sized bit mask.
 *
 * @return   none
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
void TToggleBit( short bit, unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;        // Index to byte.
    n = bit % 8;        // Specific bit in byte.

    bitmap[x] ^= TQuickByteMask[n];        // Toggle bit.
}


/** Checks specified bit.
 *
 * @return  1 if bit set else 0.
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
short TIsBitSet( short bit, const unsigned char *bitmap)
{
    short n, x;

    x = bit / 8;    // Index to byte.
    n = bit % 8;    // Specific bit in byte.

    // Test bit (logigal AND).
    if (bitmap[x] & TQuickByteMask[n])
        return 1;

    return 0;
}


/** Checks specified bit.
 *
 * @return  1 if bit reset else 0.
 *
 * @param   bit    - Bit number.
 * @param   bitmap - Pointer to bitmap.
 */
short TIsBitReset( short bit, const unsigned char *bitmap)
{
    return TIsBitSet(bit, bitmap) ^ 1;
}


/** Count number of bits set in a bitmap.
 *
 * @return   Number of bits set.
 *
 * @param    bitmap - Pointer to bitmap.
 * @param    size   - Bitmap size (in bits).
 *
 * @note    Not very efficient in terms of execution speed. If you are doing
 *        some computationally intense stuff you may need a more complex
 *        implementation which would be faster (especially for big bitmaps).
 *        See (http://graphics.stanford.edu/~seander/bithacks.html).
 */
int TCountBits( const unsigned char *bitmap, int size)
{
    int i, count = 0;

    for (i=0; i<size; i++)
        if (TIsBitSet(i, bitmap))
            count++;

    return count;
}

Beachten Sie, dass Sie Folgendes tun, um das Bit 'n' in einer 16-Bit-Ganzzahl zu setzen:

TSetBit( n, &my_int);

Es liegt an Ihnen, sicherzustellen, dass die Bitnummer im Bereich der von Ihnen übergebenen Bitmap liegt. Beachten Sie, dass für kleine Endian-Prozessoren, bei denen Bytes, Wörter, Wörter, Q-Wörter usw. im Speicher korrekt aufeinander abgestimmt sind (Hauptgrund dafür, dass kleine Endian-Prozessoren "besser" sind als Big-Endian-Prozessoren, ah, ich spüre einen Flammenkrieg auf...).

Tim Ring
quelle
2
Verwenden Sie keine Tabelle für eine Funktion, die mit einem einzelnen Operator implementiert werden kann. TQuickByteMask [n] entspricht (1 << n). Es ist auch eine sehr schlechte Idee, Ihre Argumente kurz zu fassen. Das / und% ist tatsächlich eine Division, nicht bitverschoben / bitweise und, da die vorzeichenbehaftete Division durch eine Potenz von 2 nicht bitweise implementiert werden kann. Sie sollten den Argumenttyp unsigned int machen!
R .. GitHub STOP HELPING ICE
Was bringt das? Es macht den Code nur langsamer und schwerer zu lesen? Ich kann keinen einzigen Vorteil damit sehen. 1u << n ist für C-Programmierer leichter zu lesen und kann hoffentlich in einen CPU-Befehl mit einem Takt getaktet werden. Ihre Aufteilung hingegen wird in etwa 10 Ticks oder sogar bis zu 100 Ticks übersetzt, je nachdem, wie schlecht die spezifische Architektur mit der Aufteilung umgeht. Für die Bitmap-Funktion wäre es sinnvoller, eine Nachschlagetabelle zu haben, die jeden Bitindex in einen Byte-Index übersetzt, um die Geschwindigkeit zu optimieren.
Lundin
2
Was Big / Little Endian betrifft, ordnet Big Endian Ganzzahlen und Rohdaten (z. B. Zeichenfolgen) auf dieselbe Weise zu: von links nach rechts msb bis lsb in der gesamten Bitmap. Während Little Endian Ganzzahlen von links nach rechts als 7-0, 15-8, 23-18, 31-24 abbildet, sind die Rohdaten immer noch von links nach rechts msb zu lsb. Wie wenig Endian für Ihren speziellen Algorithmus besser ist, ist mir völlig unverständlich. Es scheint das Gegenteil zu sein.
Lundin
2
@R .. Eine Tabelle kann nützlich sein, wenn sich Ihre Plattform nicht effizient verschieben lässt, wie bei alten Mikrochip-MCUs, aber dann ist die Aufteilung in der Stichprobe natürlich absolut ineffizient
jeb
12

Benutze das:

int ToggleNthBit ( unsigned char n, int num )
{
    if(num & (1 << n))
        num &= ~(1 << n);
    else
        num |= (1 << n);

    return num;
}
Peter Mortensen
quelle
5
Nun, es verwendet ineffiziente Verzweigung.
ASDF
3
@asdf Der Compiler hat die Aufgabe, die effizienteste Binärdatei auszugeben. Der Programmierer hat die Aufgabe, klaren Code zu schreiben
MM
3
Dies ist eine gute Demonstration des Testens, Einstellens und Löschens eines bestimmten Bits. Es ist jedoch ein sehr schlechter Ansatz, um ein bisschen umzuschalten.
Ben Voigt
10

Die bitsetAntwort erweitern:

#include <iostream>
#include <bitset>
#include <string>

using namespace std;
int main() {
  bitset<8> byte(std::string("10010011");

  // Set Bit
  byte.set(3); // 10010111

  // Clear Bit
  byte.reset(2); // 10010101

  // Toggle Bit
  byte.flip(7); // 00010101

  cout << byte << endl;

  return 0;
}
Kendotwill
quelle
10

Wenn Sie diese Operation mit C-Programmierung im Linux-Kernel ausführen möchten, empfehle ich die Verwendung von Standard-APIs des Linux-Kernels.

Siehe https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html

set_bit  Atomically set a bit in memory
clear_bit  Clears a bit in memory
change_bit  Toggle a bit in memory
test_and_set_bit  Set a bit and return its old value
test_and_clear_bit  Clear a bit and return its old value
test_and_change_bit  Change a bit and return its old value
test_bit  Determine whether a bit is set

Hinweis: Hier erfolgt der gesamte Vorgang in einem einzigen Schritt. Somit sind diese alle auch auf SMP-Computern garantiert atomar und nützlich, um die Kohärenz zwischen den Prozessoren aufrechtzuerhalten.

Jeegar Patel
quelle
9

Visual C 2010 und möglicherweise viele andere Compiler unterstützen direkt eingebaute Boolesche Operationen. Ein Bit hat genau wie ein Boolescher Wert zwei mögliche Werte, sodass wir stattdessen Boolesche Werte verwenden können - auch wenn sie mehr Speicherplatz beanspruchen als ein einzelnes Bit Gedächtnis in dieser Darstellung. Dies funktioniert, auch der sizeof()Bediener funktioniert ordnungsgemäß.

bool    IsGph[256], IsNotGph[256];

//  Initialize boolean array to detect printable characters
for(i=0; i<sizeof(IsGph); i++)  {
    IsGph[i] = isgraph((unsigned char)i);
}

Also, auf Ihre Frage, IsGph[i] =1oder IsGph[i] =0machen Sie das Einstellen und Löschen von Bools einfach.

So finden Sie nicht druckbare Zeichen:

//  Initialize boolean array to detect UN-printable characters, 
//  then call function to toggle required bits true, while initializing a 2nd
//  boolean array as the complement of the 1st.
for(i=0; i<sizeof(IsGph); i++)  {
    if(IsGph[i])    {
         IsNotGph[i] = 0;
    }   else   {
         IsNotGph[i] = 1;
    }
}

Beachten Sie, dass dieser Code nichts "Besonderes" ist. Es behandelt ein bisschen wie eine ganze Zahl - was es technisch ist. Eine 1-Bit-Ganzzahl, die 2 Werte und nur 2 Werte enthalten kann.

Ich habe diesen Ansatz einmal verwendet, um doppelte Darlehensdatensätze zu finden, wobei die Darlehensnummer der ISAM-Schlüssel war, wobei die 6-stellige Darlehensnummer als Index für das Bit-Array verwendet wurde. Sehr schnell und nach 8 Monaten wurde bewiesen, dass das Mainframe-System, von dem wir die Daten erhalten haben, tatsächlich fehlerhaft funktioniert. Die Einfachheit von Bit-Arrays macht das Vertrauen in ihre Korrektheit sehr hoch - im Vergleich zu einem Suchansatz zum Beispiel.

Setzen Sie Monica wieder ein
quelle
std :: bitset wird tatsächlich von den meisten Compilern als Bits implementiert
Galinette
@ Galinette, vereinbart. Die Header-Datei #include <Bitset> ist in dieser Hinsicht eine gute Ressource. Außerdem der spezielle Klassenvektor <bool> für den Fall, dass sich die Größe des Vektors ändern muss. Die C ++ STL, 2. Auflage, Nicolai M. Josuttis, behandelt sie ausführlich auf den Seiten 650 bzw. 281. C ++ 11 erweitert std :: bitset um einige neue Funktionen. Von besonderem Interesse ist für mich eine Hash-Funktion in ungeordneten Containern. Danke für die Warnung! Ich werde meinen Kommentar zu Gehirnkrämpfen löschen. Schon genug Müll im Web. Ich möchte nichts hinzufügen.
3
Dies verbraucht jeweils mindestens ein ganzes Byte Speicher bool. Vielleicht sogar 4 Bytes für C89-Setups, die intzur Implementierung verwendet werdenbool
MM
@MattMcNabb, du bist richtig. In C ++ wird die Größe des int-Typs, der zum Implementieren eines Booleschen Werts erforderlich ist, vom Standard nicht angegeben. Ich habe vor einiger Zeit festgestellt, dass diese Antwort falsch war, habe mich aber entschlossen, sie hier zu lassen, da die Leute sie anscheinend nützlich finden. Für diejenigen, die Bits verwenden möchten, ist der Kommentar von galinette am hilfreichsten, ebenso wie meine Bitbibliothek
2
@RocketRoy: Es lohnt sich wahrscheinlich, den Satz zu ändern, der behauptet, dies sei ein Beispiel für "Bitoperationen".
Ben Voigt
6

Verwenden Sie einen der hier definierten Operatoren .

Um ein Bit zu setzen, wird verwendet, int x = x | 0x?;wo ?die Bitposition in binärer Form ist.

Jason
quelle
2
0xist das Präfix für ein Literal in hexadezimaler und nicht in binärer Form.
Ben Voigt
5

Hier sind einige Makros, die ich benutze:

SET_FLAG(Status, Flag)            ((Status) |= (Flag))
CLEAR_FLAG(Status, Flag)          ((Status) &= ~(Flag))
INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
TEST_FLAGS(t,ulMask, ulBit)       (((t)&(ulMask)) == (ulBit))
IS_FLAG_SET(t,ulMask)             TEST_FLAGS(t,ulMask,ulMask)
IS_FLAG_CLEAR(t,ulMask)           TEST_FLAGS(t,ulMask,0)
sam msft
quelle
5

Variable verwendet

int value, pos;

Messwert
Datenposition - Position des Bits, das gesetzt, gelöscht oder umgeschaltet werden soll.

Setze ein bisschen:

value = value | 1 << pos;

Klar ein bisschen:

value = value & ~(1 << pos); 

Ein bisschen umschalten:

value = value ^ 1 << pos;
Jeet Parikh
quelle
5
int set_nth_bit(int num, int n){    
    return (num | 1 << n);
}

int clear_nth_bit(int num, int n){    
    return (num & ~( 1 << n));
}

int toggle_nth_bit(int num, int n){    
    return num ^ (1 << n);
}

int check_nth_bit(int num, int n){    
    return num & (1 << n);
}
Sazzad Hissain Khan
quelle
Rückgabetyp check_nth_bitkann sein bool.
Xeverous
@Xeverous ja es kommt auf die Absicht des Anrufers an
Sazzad Hissain Khan
5

Nehmen wir
num = 55 an, dass Integer zuerst einige Dinge ausführt, um bitweise Operationen auszuführen (set, get, clear, toggle).
n = 40-basierte Bitposition zum Ausführen bitweiser Operationen.

Wie komme ich ein bisschen?

  1. Um das nthbisschen num nach rechts zu verschieben num, nmal. Führen Sie dann bitweise UND &mit 1 durch.
bit = (num >> n) & 1;

Wie es funktioniert?

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

Wie setze ich ein bisschen?

  1. Ein bestimmtes Zahlenbit setzen. 1 nMal nach links verschieben . Führen Sie dann eine bitweise ODER- |Verknüpfung mit aus num.
num |= (1 << n);    // Equivalent to; num = (1 << n) | num;

Wie es funktioniert?

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

Wie lösche ich ein bisschen?

  1. Linksverschiebung 1, nmal dh 1 << n.
  2. Führen Sie eine bitweise Ergänzung mit dem obigen Ergebnis durch. Damit wird das n-te Bit nicht gesetzt und der Rest des Bits wird gesetzt, dh~ (1 << n) .
  3. Führen Sie abschließend eine bitweise UND- &Verknüpfung mit dem obigen Ergebnis und aus num. Die obigen drei Schritte zusammen geschrieben werden als num & (~ (1 << n));

Schritte zum Löschen

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

Wie es funktioniert?

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

Wie man ein bisschen umschaltet?

Um ein bisschen umzuschalten, verwenden wir den bitweisen XOR- ^Operator. Der bitweise XOR-Operator wird mit 1 ausgewertet, wenn das entsprechende Bit beider Operanden unterschiedlich ist, andernfalls mit 0.

Um ein bisschen umzuschalten, müssen wir die XOR-Operation mit dem Bit, das Sie umschalten möchten, und 1 ausführen.

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

Wie es funktioniert?

  • 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 - Bitweise Bedienerübungen

Pankaj Prakash
quelle
Danke für die ausführliche Erklärung. Hier ist der Link für das Übungsproblem für BIT Magic Link
Chandra Shekhar
4

Wie setzen, löschen und schalten Sie ein einzelnes Bit um?

Um eine häufige Codierungsfalle beim Versuch, die Maske zu bilden, zu beheben:
1ist nicht immer breit genug

Welche Probleme treten auf, wenn numberein breiterer Typ als 1?
xkann zu groß für die Verschiebung sein, 1 << xdie zu undefiniertem Verhalten (UB) führt. Auch wenn xes nicht zu groß ist, werden ~möglicherweise nicht genügend höchstwertige Bits umgedreht.

// assume 32 bit int/unsigned
unsigned long long number = foo();

unsigned x = 40; 
number |= (1 << x);  // UB
number ^= (1 << x);  // UB
number &= ~(1 << x); // UB

x = 10;
number &= ~(1 << x); // Wrong mask, not wide enough

Um 1 zu versichern, ist breit genug:

Code könnte 1ulloder pedantisch verwenden (uintmax_t)1und den Compiler optimieren lassen.

number |= (1ull << x);
number |= ((uintmax_t)1 << x);

Oder Besetzung - Dies führt zu Codierungs- / Überprüfungs- / Wartungsproblemen, wodurch die Besetzung korrekt und aktuell bleibt.

number |= (type_of_number)1 << x;

Oder fördern Sie das sanft, 1indem Sie eine mathematische Operation erzwingen, die mindestens so breit ist wie die Art von number.

number |= (number*0 + 1) << x;

Wie bei den meisten Bit - Manipulationen, am besten an die Arbeit mit unsigned Typen anstatt unterzeichnet diejenigen

chux - Monica wieder einsetzen
quelle
Interessanter Blick auf eine alte Frage! Weder number |= (type_of_number)1 << x;noch number |= (number*0 + 1) << x;angemessen, um das Vorzeichenbit eines vorzeichenbehafteten Typs zu setzen ... Tatsächlich ist es auch nicht so number |= (1ull << x);. Gibt es eine tragbare Möglichkeit, dies nach Position zu tun?
Chqrlie
1
@chqrlie IMO, der beste Weg, um das Setzen des Vorzeichenbits und das Risiko von UB oder IDB mit Verschiebungen zu vermeiden, ist die Verwendung von vorzeichenlosen Typen. Hoch portabler Shift- signierter Code ist zu kompliziert, um akzeptabel zu sein.
chux
3

Eine C ++ 11-Vorlagenversion (in einen Header eingefügt):

namespace bit {
    template <typename T1, typename T2> inline void set  (T1 &variable, T2 bit) {variable |=  ((T1)1 << bit);}
    template <typename T1, typename T2> inline void clear(T1 &variable, T2 bit) {variable &= ~((T1)1 << bit);}
    template <typename T1, typename T2> inline void flip (T1 &variable, T2 bit) {variable ^=  ((T1)1 << bit);}
    template <typename T1, typename T2> inline bool test (T1 &variable, T2 bit) {return variable & ((T1)1 << bit);}
}

namespace bitmask {
    template <typename T1, typename T2> inline void set  (T1 &variable, T2 bits) {variable |= bits;}
    template <typename T1, typename T2> inline void clear(T1 &variable, T2 bits) {variable &= ~bits;}
    template <typename T1, typename T2> inline void flip (T1 &variable, T2 bits) {variable ^= bits;}
    template <typename T1, typename T2> inline bool test_all(T1 &variable, T2 bits) {return ((variable & bits) == bits);}
    template <typename T1, typename T2> inline bool test_any(T1 &variable, T2 bits) {return variable & bits;}
}
Joakim L. Christiansen
quelle
Dieser Code ist defekt. (Auch, warum haben Sie ;nach Ihren Funktionsdefinitionen?)
Melpomene
@melpomene Der Code ist nicht kaputt, ich habe ihn getestet. Meinen Sie damit, dass es nicht kompiliert wird oder dass das Ergebnis falsch ist? Über das zusätzliche ';' Ich erinnere mich nicht, diese können tatsächlich entfernt werden.
Joakim L. Christiansen
(variable & bits == bits)?
Melpomene
Vielen Dank für das Bemerken, es sollte sein((variable & bits) == bits)
Joakim L. Christiansen
Verwendung std::bitsetin C ++ 11
pqnet
0

Dieses Programm basiert auf der obigen Lösung von @ Jeremy. Wenn jemand schnell herumspielen möchte.

public class BitwiseOperations {

    public static void main(String args[]) {

        setABit(0, 4); // set the 4th bit, 0000 -> 1000 [8]
        clearABit(16, 5); // clear the 5th bit, 10000 -> 00000 [0]
        toggleABit(8, 4); // toggle the 4th bit, 1000 -> 0000 [0]
        checkABit(8,4); // check the 4th bit 1000 -> true 
    }

    public static void setABit(int input, int n) {
        input = input | ( 1 << n-1);
        System.out.println(input);
    }


    public static void clearABit(int input, int n) {
        input = input & ~(1 << n-1);
        System.out.println(input);
    }

    public static void toggleABit(int input, int n) {
        input = input ^ (1 << n-1);
        System.out.println(input);
    }

    public static void checkABit(int input, int n) {
        boolean isSet = ((input >> n-1) & 1) == 1; 
        System.out.println(isSet);
    }
}


Output :
8
0
0
true
Balaji Boggaram Ramanarayan
quelle
-2

Versuchen Sie eine dieser Funktionen in der Sprache C, um n Bit zu ändern:

char bitfield;

// Start at 0th position

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}

Oder

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}

Oder

void chang_n_bit(int n, int value)
{
    if(value)
        bitfield |= 1 << n;
    else
        bitfield &= ~0 ^ (1 << n);
}

char get_n_bit(int n)
{
    return (bitfield & (1 << n)) ? 1 : 0;
}
Vincet
quelle
value << nkann undefiniertes Verhalten verursachen
MM