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 ^=(-(unsignedlong)x ^ number)&(1UL<< n);
oder
unsignedlong 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.
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>.
+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:
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.
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))
Ä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 :
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 { ... };
/*
** Bit set, clear, and test operations
**
** public domain snippet by Bob Stout
*/typedefenum{ERROR =-1, FALSE, TRUE} LOGICAL;#define BOOL(x)(!(!(x)))#defineBitSet(arg,posn)((arg)|(1L<<(posn)))#defineBitClr(arg,posn)((arg)&~(1L<<(posn)))#defineBitTst(arg,posn) BOOL((arg)&(1L<<(posn)))#defineBitFlp(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:
00000000000000000000000000000001 binary.
Wenn posn == 8 ist, wird Folgendes ausgewertet:
00000000000000000000000100000000 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:
#define BOOL(x)(!(!(x)))#defineBitSet(arg,posn)((arg)|(1L<<(posn)))#defineBitClr(arg,posn)((arg)&~(1L<<(posn)))#defineBitTst(arg,posn) BOOL((arg)&(1L<<(posn)))#defineBitFlp(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;}
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.
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)))))
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.
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.
structHwRegister{unsignedint errorFlag:1;// one-bit flag fieldunsignedintMode:3;// three-bit mode fieldunsignedintStatusCode:4;// four-bit status code};structHwRegister 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.
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:
int main(void){unsignedchar 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));return0;}
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.
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).
constunsignedcharTQuickByteMask[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.
*/voidTSetBit(short bit,unsignedchar*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.
*/voidTResetBit(short bit,unsignedchar*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.
*/voidTToggleBit(short bit,unsignedchar*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.
*/shortTIsBitSet(short bit,constunsignedchar*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])return1;return0;}/** Checks specified bit.
*
* @return 1 if bit reset else 0.
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/shortTIsBitReset(short bit,constunsignedchar*bitmap){returnTIsBitSet(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).
*/intTCountBits(constunsignedchar*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...).
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:
intToggleNthBit(unsignedchar n,int num ){if(num &(1<< n))
num &=~(1<< n);else
num |=(1<< n);return num;}
@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.
set_bit Atomicallyset 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 andreturn its old value
test_and_clear_bit Clear a bit andreturn its old value
test_and_change_bit Change a bit andreturn its old value
test_bit Determine whether a bit isset
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.
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äß.
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.
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.
@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?
Um das nthbisschen num nach rechts zu verschieben num, nmal. Führen Sie dann bitweise UND &mit 1 durch.
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) .
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));
num &=(~(1<< n));// Equivalent to; num = num & (~(1 << n));
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.
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/unsignedunsignedlonglong 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
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):
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.
Antworten:
Ein bisschen einstellen
Verwenden Sie den bitweisen ODER-Operator (
|
), um ein Bit zu setzen.Das wird das
n
th Bit von setzennumber
.n
sollte Null sein, wenn Sie das1
st-Bit setzen möchten und so weiter bisn-1
, wenn Sie dasn
th-Bit setzen möchten .Verwenden Sie,
1ULL
wennnumber
breiter als istunsigned long
; Die Förderung von1UL << n
erfolgt erst nach der Bewertung,1UL << n
wo es undefiniertes Verhalten ist, um mehr als die Breite von a zu verschiebenlong
. Gleiches gilt für alle übrigen Beispiele.Ein bisschen klären
Verwenden Sie den bitweisen AND-Operator (
&
), um ein Bit zu löschen.Das wird das
n
dritte bisschen klärennumber
. 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.Das wird das
n
th Bit von umschaltennumber
.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:
Dadurch wird der Wert des
n
th-Bits vonnumber
in die Variable eingefügtbit
.Ändern des n- ten Bits in x
Das Setzen des
n
th-Bits auf entweder1
oder0
kann mit den folgenden Optionen für eine C ++ - Komplementimplementierung erreicht werden:Das Bit
n
wird gesetzt, wenn esx
ist1
, und gelöscht, wenn esx
ist0
. Wennx
es einen anderen Wert gibt, erhalten Sie Müll.x = !!x
wird es auf 0 oder 1 booleanisieren.-1
Verwenden 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).oder
Im Allgemeinen ist es eine gute Idee, vorzeichenlose Typen für die Manipulation von tragbaren Bits zu verwenden.
oder
(number & ~(1UL << n))
löscht dasn
th-Bit und(x << n)
setzt dasn
th-Bit aufx
.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.
quelle
bit = (number >> x) & 1
1
ist einint
Literal, 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 besser1U
.number = number & ~(1 << n) | (x << n);
Ändern des n-ten Bits in x.Verwenden der Standard C ++ - Bibliothek :
std::bitset<N>
.Oder die Boost- Version :
boost::dynamic_bitset
.Sie müssen nicht selbst rollen:
Die Boost-Version ermöglicht ein Bit-Set in Laufzeitgröße im Vergleich zu einem Standard- Bit-Set in Kompilierungszeit für Bibliotheken .
quelle
Die andere Option ist die Verwendung von Bitfeldern:
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:
Um ein bisschen umzuschalten:
Ein bisschen nachsehen:
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.
quelle
Ich verwende Makros, die in einer Header-Datei definiert sind, um das Setzen und Löschen von Bits zu handhaben:
quelle
BITMASK_CHECK(x,y) ((x) & (y))
muss sein,((x) & (y)) == (y)
sonst gibt es ein falsches Ergebnis auf der Multibit-Maske zurück (zB5
vs.3
) / * Hallo an alle Totengräber1
sollte(uintmax_t)1
oder ähnlich sein, falls jemand versucht, diese Makros auf einemlong
oder einem größeren Typ zu verwendenBITMASK_CHECK_ALL(x,y)
kann implementiert werden als!~((~(y))|(x))
!(~(x) & (y))
Manchmal lohnt es sich, die Bits mit a
enum
zu benennen :Verwenden Sie dann die Namen später. Dh schreibe
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.
quelle
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?enum
s für Mengen verwandter Konstanten reicht in der c-Programmierung weit zurück. Ich vermute, dass bei modernen Compilern der einzige Vorteil gegenüberconst short
oder 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.enum ThingFlags
WertThingError|ThingFlag1
zum Beispiel?int
. Dies kann aufgrund impliziter Ganzzahl-Heraufstufung oder bitweiser Operationen für signierte Typen alle Arten von subtilen Fehlern verursachen.thingstate = ThingFlag1 >> 1
ruft zum Beispiel ein implementierungsdefiniertes Verhalten auf.thingstate = (ThingFlag1 >> x) << y
kann undefiniertes Verhalten aufrufen. Und so weiter. Um sicher zu gehen, verwenden Sie immer einen vorzeichenlosen Typ.enum My16Bits: unsigned short { ... };
Aus der bitops.h von snip-c.zip:
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:
Wenn posn == 8 ist, wird Folgendes ausgewertet:
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] ------------------------------------- -----------------------
---- [TEST.OUT (zerlegt)] -------------------------------------- ---------
---- [finis] --------------------------------------- ----------------------
quelle
arg
istlong long
.1L
muss der breitestmögliche Typ sein, also(uintmax_t)1
. (Sie könnten mit durchkommen1ull
)Verwenden Sie die bitweisen Operatoren:
&
|
So setzen Sie das letzte Bit
000b
:So checken Sie das letzte Bit ein
foo
:So löschen Sie das letzte Bit in
foo
:Ich habe
XXXb
aus Gründen der Klarheit verwendet. Abhängig von der Datenstruktur, in die Sie Bits packen, werden Sie wahrscheinlich mit der HEX-Darstellung arbeiten.quelle
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
Für den Anfänger möchte ich etwas mehr anhand eines Beispiels erklären:
Beispiel:
Der
&
Operator wird verwendet, um das Bit zu überprüfen:Toggle oder Flip:
|
Operator: Setzen Sie das Bitquelle
Hier ist mein Lieblingsbit-Arithmetik-Makro, das für alle Arten von vorzeichenlosen Ganzzahl-Arrays von
unsigned char
bis zusize_t
(der größte Typ, mit dem effizient gearbeitet werden sollte) funktioniert:Um ein bisschen zu setzen:
Um ein bisschen zu klären:
Um ein bisschen umzuschalten:
Um ein bisschen zu testen:
usw.
quelle
BITOP(array, bit++, |=);
in einer Schleife wird höchstwahrscheinlich nicht das tun, was der Anrufer will.BITCELL(a,b) |= BITMASK(a,b);
(beide nehmena
als Argument, um die Größe zu bestimmen, aber letztere würdena
seitdem nie ausgewertet es erscheint nur insizeof
).(size_t)
Besetzung scheint es zu sein , nur einige , um sicherzustellen , ohne Vorzeichen Mathe mit%
. Könnte(unsigned)
da sein.(size_t)(b)/(8*sizeof *(a))
könnte sichb
vor der Teilung unnötig verengen . Nur ein Problem mit sehr großen Bit-Arrays. Immer noch ein interessantes Makro.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.
quelle
volatile
und 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.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.
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.
quelle
Überprüfen Sie ein Bit an einer beliebigen Stelle in einer Variablen beliebigen Typs:
Beispielnutzung:
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.
quelle
Allgemeiner für Bitmaps beliebiger Größe:
quelle
CHAR_BIT
ist bereits definiert durchlimits.h
, müssen Sie nicht Ihre eigenenBITS
eingeben (und in der Tat machen Sie Ihren Code dadurch schlechter)Dieses Programm soll jedes Datenbit von 0 auf 1 oder 1 auf 0 ändern:
quelle
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).
Beachten Sie, dass Sie Folgendes tun, um das Bit 'n' in einer 16-Bit-Ganzzahl zu setzen:
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...).
quelle
Benutze das:
quelle
Die
bitset
Antwort erweitern:quelle
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
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.
quelle
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äß.Also, auf Ihre Frage,
IsGph[i] =1
oderIsGph[i] =0
machen Sie das Einstellen und Löschen von Bools einfach.So finden Sie nicht druckbare Zeichen:
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.
quelle
bool
. Vielleicht sogar 4 Bytes für C89-Setups, dieint
zur Implementierung verwendet werdenbool
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.quelle
0x
ist das Präfix für ein Literal in hexadezimaler und nicht in binärer Form.Hier sind einige Makros, die ich benutze:
quelle
Variable verwendet
Messwert
Datenposition - Position des Bits, das gesetzt, gelöscht oder umgeschaltet werden soll.
Setze ein bisschen:
Klar ein bisschen:
Ein bisschen umschalten:
quelle
quelle
check_nth_bit
kann seinbool
.Wie komme ich ein bisschen?
nth
bisschen num nach rechts zu verschiebennum
,n
mal. Führen Sie dann bitweise UND&
mit 1 durch.Wie es funktioniert?
Wie setze ich ein bisschen?
n
Mal nach links verschieben . Führen Sie dann eine bitweise ODER-|
Verknüpfung mit ausnum
.Wie es funktioniert?
Wie lösche ich ein bisschen?
n
mal dh1 << n
.~ (1 << n)
.&
Verknüpfung mit dem obigen Ergebnis und ausnum
. Die obigen drei Schritte zusammen geschrieben werden alsnum & (~ (1 << n))
;Wie es funktioniert?
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.
Wie es funktioniert?
0 ^ 1 => 1
.1 ^ 1 => 0
.Empfohlene Lektüre - Bitweise Bedienerübungen
quelle
Um eine häufige Codierungsfalle beim Versuch, die Maske zu bilden, zu beheben:
1
ist nicht immer breit genugWelche Probleme treten auf, wenn
number
ein breiterer Typ als1
?x
kann zu groß für die Verschiebung sein,1 << x
die zu undefiniertem Verhalten (UB) führt. Auch wennx
es nicht zu groß ist, werden~
möglicherweise nicht genügend höchstwertige Bits umgedreht.Um 1 zu versichern, ist breit genug:
Code könnte
1ull
oder pedantisch verwenden(uintmax_t)1
und den Compiler optimieren lassen.Oder Besetzung - Dies führt zu Codierungs- / Überprüfungs- / Wartungsproblemen, wodurch die Besetzung korrekt und aktuell bleibt.
Oder fördern Sie das sanft,
1
indem Sie eine mathematische Operation erzwingen, die mindestens so breit ist wie die Art vonnumber
.Wie bei den meisten Bit - Manipulationen, am besten an die Arbeit mit unsigned Typen anstatt unterzeichnet diejenigen
quelle
number |= (type_of_number)1 << x;
nochnumber |= (number*0 + 1) << x;
angemessen, um das Vorzeichenbit eines vorzeichenbehafteten Typs zu setzen ... Tatsächlich ist es auch nicht sonumber |= (1ull << x);
. Gibt es eine tragbare Möglichkeit, dies nach Position zu tun?Eine C ++ 11-Vorlagenversion (in einen Header eingefügt):
quelle
;
nach Ihren Funktionsdefinitionen?)(variable & bits == bits)
?((variable & bits) == bits)
std::bitset
in C ++ 11Dieses Programm basiert auf der obigen Lösung von @ Jeremy. Wenn jemand schnell herumspielen möchte.
quelle
Versuchen Sie eine dieser Funktionen in der Sprache C, um n Bit zu ändern:
Oder
Oder
quelle
value << n
kann undefiniertes Verhalten verursachen