Wie können Sie ein wenig einstellen, löschen und umschalten?
Wie können Sie ein wenig einstellen, löschen und umschalten?
Antworten:
Verwenden Sie den bitweisen ODER-Operator ( |
), um ein Bit zu setzen.
number |= 1UL << n;
Das wird das n
th Bit von setzen number
. n
sollte Null sein, wenn Sie das 1
st-Bit setzen möchten und so weiter bis n-1
, wenn Sie das n
th-Bit setzen möchten .
Verwenden Sie, 1ULL
wenn number
breiter als ist unsigned long
; Die Förderung von 1UL << n
erfolgt erst nach der Bewertung, 1UL << n
wo es undefiniertes Verhalten ist, um mehr als die Breite von a zu verschieben long
. Gleiches gilt für alle übrigen Beispiele.
Verwenden Sie den bitweisen AND-Operator ( &
), um ein Bit zu löschen.
number &= ~(1UL << n);
Das wird das n
dritte bisschen klären number
. Sie müssen die Bitfolge mit dem bitweisen NOT-Operator ( ~
) invertieren und dann AND it.
Mit dem XOR-Operator ( ^
) können Sie ein wenig umschalten.
number ^= 1UL << n;
Das wird das n
th Bit von umschalten number
.
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 n
th-Bits von number
in die Variable eingefügt bit
.
Das Setzen des n
th-Bits auf entweder 1
oder 0
kann mit den folgenden Optionen für eine C ++ - Komplementimplementierung erreicht werden:
number ^= (-x ^ number) & (1UL << n);
Das Bit n
wird gesetzt, wenn es x
ist 1
, und gelöscht, wenn es x
ist 0
. Wenn x
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).
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 n
th-Bit und (x << n)
setzt das n
th-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.
bit = (number >> x) & 1
1
ist ein int
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 besser 1U
.
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:
#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 .
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.
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))
BITMASK_CHECK(x,y) ((x) & (y))
muss sein, ((x) & (y)) == (y)
sonst gibt es ein falsches Ergebnis auf der Multibit-Maske zurück (zB 5
vs. 3
) / * Hallo an alle Totengräber
1
sollte (uintmax_t)1
oder ähnlich sein, falls jemand versucht, diese Makros auf einem long
oder einem größeren Typ zu verwenden
BITMASK_CHECK_ALL(x,y)
kann implementiert werden als!~((~(y))|(x))
!(~(x) & (y))
Manchmal lohnt es sich, die Bits mit a enum
zu 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.
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über const 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
Wert ThingError|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 { ... };
/*
** 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] --------------------------------------- ----------------------
arg
ist long long
. 1L
muss der breitestmögliche Typ sein, also (uintmax_t)1
. (Sie könnten mit durchkommen 1ull
)
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 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.
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
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)
Hier ist mein Lieblingsbit-Arithmetik-Makro, das für alle Arten von vorzeichenlosen Ganzzahl-Arrays von unsigned char
bis 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.
BITOP(array, bit++, |=);
in einer Schleife wird höchstwahrscheinlich nicht das tun, was der Anrufer will.
BITCELL(a,b) |= BITMASK(a,b);
(beide nehmen a
als Argument, um die Größe zu bestimmen, aber letztere würden a
seitdem nie ausgewertet es erscheint nur in sizeof
).
(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 sich b
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.
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.
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.
#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.
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)))
CHAR_BIT
ist bereits definiert durch limits.h
, müssen Sie nicht Ihre eigenen BITS
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:
{
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);
}
}
}
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...).
Benutze das:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
Die bitset
Antwort 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;
}
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.
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] =1
oder IsGph[i] =0
machen 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.
bool
. Vielleicht sogar 4 Bytes für C89-Setups, die int
zur Implementierung verwendet werdenbool
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)
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;
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);
}
check_nth_bit
kann sein bool
.
Nehmen wir
num = 55
an, dass Integer zuerst einige Dinge ausführt, um bitweise Operationen auszuführen (set, get, clear, toggle).
n = 4
0-basierte Bitposition zum Ausführen bitweiser Operationen.
nth
bisschen num nach rechts zu verschieben num
, n
mal. 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)
n
Mal 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)
n
mal dh 1 << n
.~ (1 << n)
.&
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));
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)
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?
0 ^ 1 => 1
. 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
Wie setzen, löschen und schalten Sie ein einzelnes Bit um?
Um eine häufige Codierungsfalle beim Versuch, die Maske zu bilden, zu beheben:
1
ist nicht immer breit genug
Welche Probleme treten auf, wenn number
ein breiterer Typ als 1
?
x
kann zu groß für die Verschiebung sein, 1 << x
die zu undefiniertem Verhalten (UB) führt. Auch wenn x
es 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 1ull
oder pedantisch verwenden (uintmax_t)1
und 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, 1
indem 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
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?
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;}
}
;
nach Ihren Funktionsdefinitionen?)
(variable & bits == bits)
?
((variable & bits) == bits)
std::bitset
in C ++ 11
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
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;
}
value << n
kann undefiniertes Verhalten verursachen