[c++] Wie können Sie ein einzelnes Bit setzen, löschen und umschalten?



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 Laufzeit-Bitset im Vergleich zu einem Standard- Kompilierungszeit-Bitset der Bibliothek .

Question

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




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



Wenn Sie viel drehen, können Sie Masken verwenden, die das Ganze schneller machen. Die folgenden Funktionen sind sehr schnell und immer noch flexibel (sie ermöglichen Bit-Bitdling 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;
}

Hinweis: Um das Bit 'n' in einer 16-Bit-Ganzzahl zu setzen, gehen Sie folgendermaßen vor:

TSetBit( n, &my_int);

Es liegt an Ihnen, sicherzustellen, dass die Bitnummer innerhalb des Bereichs der Bitmap liegt, die Sie übergeben. Beachten Sie, dass für Little-Endian-Prozessoren, Bytes, Wörter, Dwords, Qwords, usw., einander korrekt zugeordnet werden (Hauptgrund dafür, dass Little-Endian-Prozessoren 'besser' sind als Big-Endian-Prozessoren, ah, ich spüre einen Flammenkrieg auf...).




Verwenden Sie die bitweisen Operatoren: & |

Um das letzte Bit in 000b :

foo = foo | 001b

Um das letzte Bit in foo zu überprüfen:

if ( foo & 001b ) ....

Um das letzte Bit in foo zu löschen:

foo = foo & 110b

Ich habe XXXb für Klarheit verwendet. Sie werden wahrscheinlich mit der HEX-Darstellung arbeiten, abhängig von der Datenstruktur, in die Sie Bits packen.




Ich benutze Makros, die in einer Header-Datei definiert sind, um Bit-Set und Clear zu behandeln:

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

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



Da dies mit "embedded" gekennzeichnet ist, nehme ich an, dass Sie einen Mikrocontroller verwenden. Alle obigen Vorschläge sind gültig und funktionieren (read-modify-write, unions, structs usw.).

Bei einem Oszilloskop-basierten Debugging stellte ich jedoch mit Erstaunen fest, dass diese Methoden im Vergleich zu einem PORTnSET / PORTnCLEAR-Register des Mikros einen beträchtlichen Mehraufwand im Vergleich zum Schreiben eines Wertes haben, was einen echten Unterschied macht, wenn enge Schleifen / hoch sind der ISR-Umschaltknöpfe.

Für diejenigen, die nicht vertraut sind: In meinem Beispiel hat das Mikro ein allgemeines Pin-Status-Register PORTn, das die Ausgangs-Pins reflektiert, so dass dies PORTn | = BIT_TO_SET zu einem Read-Modify-Write zu diesem Register führt. Die PORTnSET / PORTnCLEAR-Register nehmen jedoch eine "1", um "Bitte machen Sie dieses Bit 1" (SET) oder "bitte machen dieses Bit Null" (CLEAR) und eine "0" zu bedeuten "Lassen Sie den Pin in Ruhe". Sie erhalten also zwei Portadressen, je nachdem, ob Sie das Bit setzen oder löschen (nicht immer bequem), aber eine viel schnellere Reaktion und kleinerer zusammengesetzter Code.




Visual C 2010, and perhaps many other compilers, have direct support for bit operations built in. Surprisingly, this works, even the sizeof() operator works properly.

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

So, to your question, IsGph[i] =1, or IsGph[i] =0 make setting and clearing bools easy.

To find unprintable characters...

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

Note there is nothing "special" about this code. It treats a bit like an integer - which technically, it is. A 1 bit integer that can hold 2 values, and 2 values only.

I once used this approach to find duplicate loan records, where loan_number was the ISAM key, using the 6-digit loan number as an index into the bit array. Savagely fast, and after 8 months, proved that the mainframe system we were getting the data from was in fact malfunctioning. The simplicity of bit arrays makes confidence in their correctness very high - vs a searching approach for example.




How do you set, clear, and toggle a single bit?

To address a common coding pitfall when attempting to form the mask:
1 is not always wide enough

What problems happen when number is a wider type than 1 ?
x may be too great for the shift 1 << x leading to undefined behavior (UB). Even if x is not too great, ~ may not flip enough most-significant-bits.

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

To insure 1 is wide enough:

Code could use 1ull or pedantically (uintmax_t)1 and let the compiler optimize.

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

Or cast - which makes for coding/review/maintenance issues keeping the cast correct and up-to-date.

number |= (type_of_number)1 << x;

Or gently promote the 1 by forcing a math operation that is as least as wide as the type of number .

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

As with most bit manipulations, best to work with unsigned types rather than signed ones




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



Probieren Sie eine dieser Funktionen in der C-Sprache aus, 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;
}



Aus snip-c.zip 's 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 allgemeine Ausdruck, mit dem Sie Probleme zu haben scheinen, ist "(1L << (posn))". All dies erstellt eine Maske mit einem einzigen Bit, das mit jedem Integer-Typ arbeitet. Das Argument "posn" gibt die Position an, an der das Bit liegen soll. Wenn posn == 0 ist, wird dieser Ausdruck wie folgt ausgewertet:

    0000 0000 0000 0000 0000 0000 0000 0001 binary.

Wenn posn == 8, wird es ausgewertet

    0000 0000 0000 0000 0000 0001 0000 0000 binary.

Mit anderen Worten, es erzeugt einfach ein Feld von Nullen mit einer 1 an der spezifizierten Position. Der einzige knifflige Teil ist im Makro BitClr (), wo wir ein einzelnes Bit 0 in einem Feld von 1 setzen müssen. Dies wird erreicht, indem das 1-Komplement des gleichen Ausdrucks verwendet wird, wie er durch den Tilde (~) Operator angegeben ist.

Sobald die Maske erstellt ist, wird sie wie von Ihnen vorgeschlagen auf das Argument angewendet, indem Sie die bitweisen und (&) - oder (|) - und xor (^) -Operatoren verwenden. Da die Maske vom Typ long ist, funktionieren die Makros genauso gut bei char's, short's, int's oder longs.

Die Quintessenz 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 mit expliziten Maskenwerten jedes Mal neu zu schreiben, wenn Sie eines benötigen, aber warum? Denken Sie daran, dass die Makrosubstitution im Präprozessor stattfindet und der generierte Code die Tatsache widerspiegelt, dass die Werte vom Compiler als konstant betrachtet werden - dh es ist genauso effizient, die verallgemeinerten Makros zu verwenden, um das Rad jedes Mal neu zu erfinden mach etwas Manipulation.

Nicht überzeugt? Hier ist ein Testcode - Ich habe Watcom C mit voller Optimierung und ohne Verwendung von _cdecl verwendet, so dass die resultierende Disassemblierung so sauber wie möglich sein würde:

---- [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 (demontiert)] -------------------------------------- ---------

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




Dieses Programm soll jedes Datenbit von 0 auf 1 oder von 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);
        }
    }
}



Use one of the operators as defined here .

To set a bit, used int x = x | 0x?; where ? is the bit position in binary form.




Related