setzen - c++ bit



Wie können Sie ein einzelnes Bit setzen, löschen und umschalten? (18)

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

https://code.i-harness.com


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

Um bei der Bildung der Maske eine häufige Codierungsfalle zu berücksichtigen:
1ist nicht immer breit genug

Welche Probleme treten auf, wenn numberein breiterer Typ ist als 1?
xist möglicherweise zu groß für die Verschiebung, 1 << xdie zu undefiniertem Verhalten (UB) führt. Selbst wenn xes nicht zu groß ist, werden ~möglicherweise die höchstwertigen Bits nicht genug gewendet.

// 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 zu versichern, 1 ist breit genug:

Code könnte 1ullpedantisch verwendet werden (uintmax_t)1und den Compiler optimieren lassen.

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

Oder Besetzung - was für Probleme bei der Codierung / Überprüfung / Wartung sorgt, um die Besetzung auf dem neuesten Stand zu halten.

number |= (type_of_number)1 << x;

Oder fördern Sie sanft das 1Erzwingen einer mathematischen Operation, die mindestens so breit ist wie der Typ von number.

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

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


Ein bisschen einstellen

Verwenden Sie den bitweisen OR-Operator ( | ), um ein Bit festzulegen.

number |= 1UL << n;

Damit wird das n te Bit der number . n sollte Null sein, wenn Sie das 1 Bit usw. setzen möchten, bis n-1 , wenn Sie das n te Bit setzen möchten.

Verwenden Sie 1ULL wenn die number breiter als die unsigned long . Die Förderung von 1UL << n erfolgt erst, nachdem 1UL << n wobei es undefiniertes Verhalten ist, sich um mehr als die Breite eines long . Gleiches gilt für alle übrigen Beispiele.

Ein bisschen klären

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

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

Dadurch wird das n te Bit number . Sie müssen den Bitstring mit dem bitweisen NOT-Operator ( ~ ) und dann mit AND invertieren.

Ein bisschen umschalten

Der XOR-Operator ( ^ ) kann verwendet werden, um ein Bit umzuschalten.

number ^= 1UL << n;

Dadurch wird das n te Bit der number umgeschaltet.

Ein bisschen überprüfen

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

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

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

Dadurch wird der Wert des n ten Bits in das Variablenbit bit .

Ändern des n- ten Bits in x

Das Setzen des n ten Bits auf 1 oder 0 kann mit der folgenden Implementierung einer 2-er Komplement-C ++ - Implementierung erreicht werden:

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

Bit n wird gesetzt, wenn x 1 , und gelöscht, wenn x 0 . Wenn x einen anderen Wert hat, erhalten Sie Müll. x = !!x booleanize es auf 0 oder 1.

Um dies vom Zweierkomplement-Negationsverhalten unabhängig zu machen (wobei bei -1 alle Bits gesetzt sind, im Gegensatz zu einer 1-Komplement- oder Vorzeichen- / Größen-C ++ - Implementierung), verwenden Sie die vorzeichenlose Negation.

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

oder

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

Im Allgemeinen ist es eine gute Idee, für die portable Bit-Bearbeitung unsignierte Typen zu verwenden.

Im Allgemeinen ist es auch eine gute Idee, Code im Allgemeinen nicht zu kopieren / einzufügen. Daher verwenden viele Leute Präprozessor-Makros (wie die Community-Wiki-Antwort weiter unten ) oder eine Art Verkapselung.


Aus snip-c.zip -c.zips bitops.h:

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

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

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

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

OK, lass uns die Dinge analysieren ...

Der häufigste Ausdruck, mit dem Sie Probleme zu haben scheinen, lautet "(1L << (Posn))". Dazu erstellen Sie eine Maske mit einem einzigen Bit, die mit jedem Integer-Typ funktioniert. Das Argument "posn" gibt die Position an, an der Sie das Bit haben möchten. Wenn posn == 0 ist, wird dieser Ausdruck folgendermaßen ausgewertet:

    0000 0000 0000 0000 0000 0000 0000 0001 binary.

Wenn posn == 8, wird es zu ausgewertet

    0000 0000 0000 0000 0000 0001 0000 0000 binary.

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

Nachdem die Maske erstellt wurde, wird sie auf das Argument genauso angewendet, wie Sie es vorgeschlagen haben, indem Sie die Operatoren bitweise und (&) oder (|) und xor (^) verwenden. Da die Maske vom Typ long ist, funktionieren die Makros genauso gut bei Zeichen, kurzen, int's oder longs.

Unter dem Strich ist dies eine allgemeine Lösung für eine ganze Klasse von Problemen. Es ist natürlich möglich und sogar angebracht, das Äquivalent eines dieser Makros jedes Mal mit expliziten Maskenwerten umzuschreiben, wenn Sie einen benötigen, aber warum? Denken Sie daran, dass die Makrosubstitution im Präprozessor erfolgt, sodass der generierte Code die Tatsache widerspiegelt, dass die Werte vom Compiler als konstant betrachtet werden. Das heißt, es ist genauso effizient, die verallgemeinerten Makros zu verwenden, als das Rad jedes Mal neu zu "erfinden" etwas manipulieren.

Nicht überzeugt? Hier ist ein Testcode - ich habe Watcom C mit voller 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] --------------------------------------- ----------------------


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

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

Während eines Oszilloskop-basierten Debugging war ich jedoch erstaunt, als ich herausfand, dass diese Methoden einen erheblichen Overhead in CPU-Zyklen verursachen, verglichen mit dem direkten Schreiben eines Werts in die PORTnSET / PORTnCLEAR-Register des Micro -frequente ISR-Schaltstifte.

Für diejenigen, die nicht vertraut sind: In meinem Beispiel hat das Mikro ein allgemeines Pin-State-Register PORTn, das die Ausgangspins widerspiegelt. Wenn Sie PORTn | = BIT_TO_SET verwenden, führt dies zu einem Read-Modify-Write in dieses Register. Die PORTnSET / PORTnCLEAR-Register nehmen jedoch eine '1' für "bitte machen Sie dieses Bit auf 1" (SET) oder "bitte machen Sie dieses Bit auf Null" (CLEAR) und eine "0" auf "den Pin in Ruhe lassen". Am Ende stehen also zwei Portadressen zur Verfügung, je nachdem, ob Sie das Bit setzen oder löschen (nicht immer bequem), aber eine viel schnellere Reaktion und weniger zusammengesetzten Code.


Der Bitfield-Ansatz hat weitere Vorteile in der Embedded-Arena. Sie können eine Struktur definieren, die direkt auf die Bits in einem bestimmten Hardware-Register 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 Reihenfolge der Bitpackung bewusst sein - ich denke, es ist zuerst MSB, aber dies kann von der Implementierung abhängen. Überprüfen Sie auch, wie die Felder Ihres Compilers Byte-Grenzen überschreiten.

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


Dieses Programm kann 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);
        }
    }
}

Für den Anfänger möchte ich mit einem Beispiel etwas mehr 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: das Bit setzen

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

Ich benutze Makros, die in einer Header-Datei definiert sind, um das Bit-Set zu behandeln und zu löschen:

/* 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))

Manchmal ist es enum , die Bits mit einem enum zu benennen :

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

Verwenden Sie die Namen dann später. Ich 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.

Abgesehen davon stimme ich Jeremys Lösung zu.


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

Oder die Boost Version: boost::dynamic_bitset .

Es ist nicht nötig, selbst zu rollen:

#include <bitset>
#include <iostream>

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

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

    std::cout << x << std::endl;
}
[Alpha:] > ./a.out
00010

Die Boost-Version ermöglicht ein Bitset mit Laufzeitgröße im Vergleich zu einem Standardbibliothek mit Kompilierungszeit.


Wenn Sie viel herumspielen, möchten Sie vielleicht Masken verwenden, die das Ganze schneller machen. Die folgenden Funktionen sind sehr schnell und dennoch flexibel (sie ermöglichen Bit-Twiddling in Bitmaps beliebiger 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;
}

Um das Bit 'n' in einer 16-Bit-Ganzzahl zu setzen, führen Sie die folgenden Schritte aus:

TSetBit( n, &my_int);

Sie müssen sicherstellen, dass sich die Bit-Nummer innerhalb des Bereichs der Bitmap befindet, die Sie übergeben. Beachten Sie, dass für kleine Endian-Prozessoren, die Bytes, Wörter, Dwords, Qwords usw. im Speicher richtig zugeordnet werden (der Hauptgrund dafür, dass Little-Endian-Prozessoren "besser" sind als Big-Endian-Prozessoren) auf...).


Hier sind einige Makros, die ich verwende:

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;

value -
Datenposition - Position des Bits, das wir setzen, löschen oder umschalten
möchten Bit setzen

value = value | 1 << pos;

Klar ein bisschen

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

Ein wenig umschalten

value = value ^ 1 << pos;

Visual C 2010 und möglicherweise viele andere Compiler verfügen über eine direkte Unterstützung für integrierte Bitoperationen. Überraschenderweise funktioniert dies, auch wenn der Operator sizeof () ordnungsgemäß funktioniert.

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, zu Ihrer Frage machen IsGph [i] = 1 oder IsGph [i] = 0 das Setzen und Löschen von Bools einfach.

Nicht druckbare Zeichen finden ...

//  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 an diesem Code nichts "Besonderes" ist. Es behandelt ein bisschen wie eine ganze Zahl - was technisch ist. Eine 1-Bit-Ganzzahl, die 2 Werte und nur 2 Werte aufnehmen kann.

Ich habe diesen Ansatz einmal verwendet, um doppelte Darlehenssätze zu finden, wobei loan_number der ISAM-Schlüssel war und die 6-stellige Darlehensnummer als Index für das Bit-Array verwendet. Schnell und nach acht Monaten bewies das Mainframe-System, von dem wir die Daten erhielten, tatsächlich nicht richtig. Die Einfachheit von Bit-Arrays macht das Vertrauen in ihre Korrektheit sehr hoch - im Vergleich zum Beispiel zum Suchen.


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;
}

Wenn Sie diese gesamte Operation mit C-Programmierung im Linux-Kernel durchfü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 die gesamte Operation in einem einzigen Schritt. Daher sind diese Eigenschaften auch auf SMP-Computern garantiert atomar und sorgen für die Kohärenz zwischen den Prozessoren.


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);
}




bitwise-operators