c++ manipulation bitmaskierung - Wie können Sie ein einzelnes Bit setzen, löschen und umschalten?




13 Answers

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.

multiple bits arduino

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




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



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




Verwenden Sie die bitweisen Operatoren: & |

Letztes Bit in 000b :

foo = foo | 001b

So überprüfen Sie das letzte Bit in foo :

if ( foo & 001b ) ....

Um das letzte Stück in foo zu löschen:

foo = foo & 110b

Ich habe XXXb zur Verdeutlichung verwendet. Sie arbeiten wahrscheinlich mit der HEX-Darstellung, abhängig von der Datenstruktur, in die Sie Bits packen.




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.




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



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




Benutze das:

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

    return num;
}



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.




Verwenden Sie einen der here definierten Operatoren .

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




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




Eine in C ++ 11 erstellte Version (in eine Kopfzeile einfügen):

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



Versuchen Sie eine dieser Funktionen in der Sprache C, um das 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;
}



Related