Вопрос: Как вы устанавливаете, очищаете и переключаете один бит?


Как вы устанавливаете, очищаете и переключаете бит в C / C ++?


2014


источник


Ответы:


Установка бит

Используйте побитовый оператор OR ( |), чтобы установить бит.

number |= 1UL << x;

Это установит бит x,

использование 1ULLесли numberшире, чем unsigned long; продвижение 1UL << xне происходит до тех пор, пока оценка 1UL << xгде неопределенное поведение сдвигается больше, чем ширина long, То же самое относится ко всем остальным примерам.

Очистка бит

Используйте побитовый оператор И ( &), чтобы очистить бит.

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

Это очистит бит x, Вы должны инвертировать битовую строку с помощью побитового оператора NOT ( ~), тогда И это.

Переключение немного

Оператор XOR ( ^) можно использовать для переключения бит.

number ^= 1UL << x;

Это приведет к переключению бит x,

Проверка бит

Вы не просили об этом, но я мог бы также добавить его.

Чтобы проверить бит, сдвиньте число x вправо, затем побитовое И это:

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

Это положит значение бит xв переменную bit,

Изменение N го бита до Икс

Настройка nго бита в любой 1или 0может быть достигнуто с помощью следующей реализации C ++ дополнения C ++:

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

Немного nбудет установлено, если xявляется 1, и очищается, если xявляется 0, Если xимеет некоторую другую ценность, вы получаете мусор. x = !!xбудет буферизовать его до 0 или 1.

Сделать это независимым от поведения отрицательного дополнения 2 (где -1имеет все установленные биты, в отличие от реализации дополнения 1 или знака / величины C ++), используйте отрицание без знака.

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

или

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

Как правило, неплохо использовать неподписанные типы для переносимых манипуляций с битами.

В целом также неплохо не копировать / вставлять код в целом, и поэтому многие люди используют макросы препроцессора (например, сообщество wiki ответит дальше ) или некоторая инкапсуляция.


2957



Использование стандартной библиотеки C ++: std::bitset<N>,

Или Увеличение версия: boost::dynamic_bitset,

Нет необходимости катиться самостоятельно:

#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

Версия Boost позволяет устанавливать бит-бит по размеру по сравнению с стандартная библиотека компиляционный бит.


371



Другой вариант - использовать битовые поля:

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

определяет 3-битное поле (на самом деле это три однобитовых поля). Бит-операции теперь становятся немного (ха-ха) проще:

Чтобы установить или очистить бит:

mybits.b = 1;
mybits.c = 0;

Чтобы переключить бит:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

Проверка немного:

if (mybits.c)  //if mybits.c is non zero the next line below will execute

Это работает только с битовыми полями фиксированного размера. В противном случае вам придется прибегнуть к методам бит-кручения, описанным в предыдущих сообщениях.


207



I use macros defined in a header file to handle bit set and clear:

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

121



It is sometimes worth using an enum to name the bits:

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

Then use the names later on. I.e. write

thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}

to set, clear and test. This way you hide the magic numbers from the rest of your code.

Other than that I endorse Jeremy's solution.


95



From 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, let's analyze things...

The common expression that you seem to be having problems with in all of these is "(1L << (posn))". All this does is create a mask with a single bit on and which will work with any integer type. The "posn" argument specifies the position where you want the bit. If posn==0, then this expression will evaluate to:

    0000 0000 0000 0000 0000 0000 0000 0001 binary.

If posn==8, it will evaluate to

    0000 0000 0000 0000 0000 0001 0000 0000 binary.

In other words, it simply creates a field of 0's with a 1 at the specified position. The only tricky part is in the BitClr() macro where we need to set a single 0 bit in a field of 1's. This is accomplished by using the 1's complement of the same expression as denoted by the tilde (~) operator.

Once the mask is created it's applied to the argument just as you suggest, by use of the bitwise and (&), or (|), and xor (^) operators. Since the mask is of type long, the macros will work just as well on char's, short's, int's, or long's.

The bottom line is that this is a general solution to an entire class of problems. It is, of course, possible and even appropriate to rewrite the equivalent of any of these macros with explicit mask values every time you need one, but why do it? Remember, the macro substitution occurs in the preprocessor and so the generated code will reflect the fact that the values are considered constant by the compiler - i.e. it's just as efficient to use the generalized macros as to "reinvent the wheel" every time you need to do bit manipulation.

Unconvinced? Here's some test code - I used Watcom C with full optimization and without using _cdecl so the resulting disassembly would be as clean as possible:

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

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


31



For the beginner I would like to explain a bit more with an example:

Example:

value is 0x55;
bitnum : 3rd.

The & operator is used check the bit:

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 or Flip:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)

| operator: set the bit

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

27



Use the bitwise operators: & |

To set last bit in 000b:

foo = foo | 001b

To check last bit in foo:

if ( foo & 001b ) ....

To clear last bit in foo:

foo = foo & 110b

I used XXXb for clarity. You'll probably be working with HEX representation, depending on the data structure in which you're packing bits.


24



Here's my favorite bit arithmetic macro, which works for any type of unsigned integer array from unsigned char up to size_t (which is the biggest type that should be efficient to work with):

#define BITOP(a,b,op) \
 ((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))

To set a bit:

BITOP(array, bit, |=);

To clear a bit:

BITOP(array, bit, &=~);

To toggle a bit:

BITOP(array, bit, ^=);

To test a bit:

if (BITOP(array, bit, &)) ...

etc.


22



As this is tagged "embedded" I'll assume you're using a microcontroller. All of the above suggestions are valid & work (read-modify-write, unions, structs, etc.).

However, during a bout of oscilloscope-based debugging I was amazed to find that these methods have a considerable overhead in CPU cycles compared to writing a value directly to the micro's PORTnSET / PORTnCLEAR registers which makes a real difference where there are tight loops / high-frequency ISR's toggling pins.

For those unfamiliar: In my example, the micro has a general pin-state register PORTn which reflects the output pins, so doing PORTn |= BIT_TO_SET results in a read-modify-write to that register. However, the PORTnSET / PORTnCLEAR registers take a '1' to mean "please make this bit 1" (SET) or "please make this bit zero" (CLEAR) and a '0' to mean "leave the pin alone". so, you end up with two port addresses depending whether you're setting or clearing the bit (not always convenient) but a much faster reaction and smaller assembled code.


20



The bitfield approach has other advantages in the embedded arena. You can define a struct that maps directly onto the bits in a particular hardware register.

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;

You need to be aware of the bit packing order - I think it's MSB first, but this may be implementation-dependent. Also, verify how your compiler handlers fields crossing byte boundaries.

You can then read, write, test the individual values as before.


19