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

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

1812
задан JeffV 07 сент. '08 в 3:42
источник поделиться

24 ответов

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

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

number |= 1UL << x;

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

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

Очистка бит

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

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

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

Переключение бит

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

number ^= 1UL << x;

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

Проверка бит

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

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

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

Это приведет к изменению значения бит x в переменной bit.

Изменение n-го бита на x

Установка n -го бита на 1 или 0 может быть достигнута с помощью следующего в реализации С++ с дополнением 2:

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

Бит n будет установлен, если x равен 1, и очищается, если x - 0. Если x имеет другое значение, вы получаете мусор. x = !!x будет генерировать booleanize до 0 или 1.

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

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

или

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

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

2763
ответ дан Jeremy Ruten 07 сент. '08 в 3:50
источник поделиться

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

Или Boost версия: 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 позволяет устанавливать бит-бит по размеру по сравнению с стандартной библиотекой, компилируемым по времени.

345
ответ дан Loki Astari 18 сент. '08 в 3:34
источник поделиться

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

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

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

193
ответ дан Ferruccio 11 сент. '08 в 3:56
источник поделиться

Я использую макросы, определенные в файле заголовка, для обработки набора бит и очистки:

/* 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))
113
ответ дан Steve Karg 05 нояб. '08 в 1:35
источник поделиться

Иногда стоит использовать enum для обозначения битов:

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

Затем используйте имена позже. То есть написать

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

установить, очистить и проверить. Таким образом вы скрываете магические числа от остальной части вашего кода.

Кроме того, я поддерживаю решение Джереми.

88
ответ дан dmckee 09 сент. '08 в 0:07
источник поделиться

От snip-c.zip bitops.how:

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

Хорошо, пусть анализирует вещи...

Общее выражение во всех этих аспектах, в которых вы, кажется, испытываете проблемы с is "(1L < (posn))". Все это делает создание маски с одним битом на и который будет работать с любым целым типом. Аргумент "posn" указывает где вы хотите бит. Если posn == 0, то это выражение будет оценить:

    0000 0000 0000 0000 0000 0000 0000 0001 binary.

Если posn == 8, он будет оценивать

    0000 0000 0000 0000 0000 0001 0000 0000 binary.

Другими словами, он просто создает поле 0 с 1 в указанном должность. Единственная сложная часть - в макросе BitClr(), где нам нужно установить один 0 бит в поле 1. Это достигается с помощью 1-го дополнение того же выражения, которое обозначается оператором тильды (~).

После создания маски она применяется к аргументу так же, как вы предлагаете, с использованием поразрядных и (&), или (|), и xor (^) операторов. Поскольку маска имеет тип long, макросы будут работать так же хорошо на char, shorts, int, или длинный.

Суть в том, что это общее решение для всего класса проблемы. Конечно, возможно и даже целесообразно переписать эквивалент любого из этих макросов с явными значениями маски каждый раз, когда вы нужен один, но зачем? Помните, что макроподстановка происходит в препроцессор, и поэтому сгенерированный код будет отражать тот факт, что значения считаются постоянными компилятором, т.е. так же эффективно использовать обобщенные макросы, чтобы "изобретать велосипед" каждый раз, когда вам нужно делать бит.

убежденный? Здесь некоторый тестовый код - я использовал Watcom C с полной оптимизацией и без использования _cdecl, поэтому результирующая разборка будет такой же чистой, как и возможно:

---- [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(разобрано)] ----------------------------------- ------------

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

29
ответ дан yogeesh 17 сент. '08 в 5:04
источник поделиться

Для новичка я хотел бы еще немного пояснить пример:

Пример:

value is 0x55;
bitnum : 3rd.

Используется оператор &, проверяющий бит:

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)

Переключить или перевернуть:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)
Оператор

|: установите бит

0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
25
ответ дан kapilddit 05 июня '12 в 17:18
источник поделиться

Используйте побитовые операторы: & |

Чтобы установить последний бит в 000b:

foo = foo | 001b

Чтобы проверить последний бит в foo:

if ( foo & 001b ) ....

Чтобы очистить последний бит в foo:

foo = foo & 110b

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

22
ответ дан nsanders 07 сент. '08 в 3:45
источник поделиться

Здесь мой любимый битовый арифметический макрос, который работает для любого типа беззнакового целочисленного массива от unsigned char до size_t (который является самым большим типом, который должен быть эффективен для работы):

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

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

BITOP(array, bit, |=);

Чтобы очистить бит:

BITOP(array, bit, &=~);

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

BITOP(array, bit, ^=);

Чтобы проверить бит:

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

и др.

21
ответ дан R.. 13 июля '10 в 9:53
источник поделиться

Поскольку это помечено как "встроенное", я предполагаю, что вы используете микроконтроллер. Все приведенные выше предложения действительны и работают (read-modify-write, union, structs и т.д.).

Однако во время оспаривания на основе осциллографа я был поражен, обнаружив, что эти методы имеют значительные накладные расходы в цикле процессора по сравнению с записью значения непосредственно в регистры micro PORTnSET/PORTnCLEAR, что делает реальную разницу там, где есть петли/высокочастотные переключающие контакты ISR.

Для тех, кто не знаком: В моем примере микро имеет общий регистр PORTn, который отображает выходные выводы, поэтому PORTn | = BIT_TO_SET приводит к чтению-изменению-записи в этот регистр. Однако регистры PORTnSET/PORTnCLEAR принимают значение "1", чтобы означать "пожалуйста, сделайте этот бит 1" (SET) или "пожалуйста, сделайте этот бит ноль" (CLEAR) и "0", чтобы означать "оставьте один вывод". поэтому вы получаете два порта, в зависимости от того, устанавливаете ли вы или очищаете бит (не всегда удобно), но гораздо быстрее реагируете и уменьшаете собранный код.

20
ответ дан John U 14 июня '12 в 18:23
источник поделиться

Более общие для растровых изображений произвольного размера:

#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)))
17
ответ дан bill 14 июня '09 в 0:27
источник поделиться

Прикладной подход имеет другие преимущества во встроенной арене. Вы можете определить структуру, которая непосредственно сопоставляется с битами в конкретном регистре оборудования.

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;

Вам нужно знать порядок упаковки бит: сначала я думаю, что это MSB, но это может быть зависимым от реализации. Кроме того, проверьте, как поля обработчиков ваших обработчиков пересекают границы байтов.

Затем вы можете читать, писать, тестировать отдельные значения, как и раньше.

17
ответ дан Roddy 06 нояб. '08 в 14:30
источник поделиться

Проверьте бит в произвольном месте в переменной произвольного типа:

#define bit_test(x, y)  ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )

Использование примера:

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

Примечания: Это спроектировано так, чтобы быть быстрым (учитывая его гибкость) и не разветвленным. Это приводит к эффективному программному коду SPARC при компиляции Sun Studio 8; Я также тестировал его с помощью MSVС++ 2008 на amd64. Можно создать аналогичные макросы для установки и очистки бит. Ключевое отличие этого решения по сравнению со многими другими здесь заключается в том, что он работает для любого местоположения практически в любом типе переменных.

15
ответ дан John Zwinck 04 янв. '09 в 2:44
источник поделиться

Используйте это:

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

    return num;
}
11
ответ дан thangavel 12 апр. '09 в 2:05
источник поделиться

Если вы делаете много бит, вы можете использовать маски, которые ускорят все это. Следующие функции очень быстрые и гибкие (они позволяют свертывание бит в битовых картах любого размера).

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

Примечание. Чтобы установить бит 'n' в 16-битное целое число, вы делаете следующее:

TSetBit (n, & my_int);

Это зависит от вас, чтобы убедиться, что номер бит находится в пределах диапазона бит-карты, которую вы передаете. Обратите внимание, что для маленьких процессоров endian, которые байты, слова, слова, qwords и т.д. Правильно отображают друг друга в памяти (основная причина, по которой маленькие процессоры endian "лучше", чем процессоры большого числа, ах, я чувствую, что идет пламенная война...).

11
ответ дан Tim Ring 17 сент. '08 в 17:10
источник поделиться

Эта программа предназначена для изменения любого бита данных от 0 до 1 или от 1 до 0:

{
    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);
        }
    }
}
10
ответ дан Gokul Naathan 28 февр. '12 в 22:27
источник поделиться

Если вы хотите выполнить эту операцию с программированием c в Linux-ядре, я предлагаю использовать стандартный apis ядра linux.

См. 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 

Примечание. Здесь вся операция выполняется за один шаг, поэтому все они гарантированы как атомные даже на SMP-компьютерах и полезны для обеспечения согласованности между процессорами.

8
ответ дан Jeegar Patel 27 мая '16 в 19:41
источник поделиться

Visual C 2010 и, возможно, многие другие компиляторы имеют прямую поддержку встроенных битовых операций. Удивительно, но это работает, даже оператор sizeof() работает правильно.

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

Итак, на ваш вопрос IsGph [i] = 1 или IsGph [i] = 0 упростить настройку и очистку bools.

Найти непечатаемые символы...

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

Обратите внимание, что в этом коде нет ничего особенного. Он обрабатывает бит как целое, что технически, это так. 1-битное целое число, которое может содержать 2 значения и только 2 значения.

Когда-то я использовал этот подход для поиска дублирующих записей кредита, где номер_суммы был ключом ISAM, используя 6-значный номер кредита в качестве индекса в бит-массиве. Непосредственно быстро, и через 8 месяцев доказал, что система мейнфреймов, из которой мы получали данные, фактически неисправна. Простота бит-массивов делает уверенность в их правильности очень высокой - например, под поисковым подходом.

7
ответ дан RocketRoy 30 дек. '12 в 5:31
источник поделиться

Расширение ответа bitset:

#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;
}
6
ответ дан kendotwill 08 мая '14 в 7:33
источник поделиться

Вот некоторые макросы, которые я использую:

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)
5
ответ дан sam msft 07 февр. '15 в 2:11
источник поделиться

Используйте один из операторов, как определено здесь.

Чтобы установить бит, используйте int x = x | 0x?;, где ? - это битовая позиция в двоичной форме.

5
ответ дан Jason 30 апр. '12 в 9:48
источник поделиться

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

Чтобы решить проблему с общей ошибкой кодирования при попытке сформировать маску:
1 не всегда достаточно широко

Какие проблемы возникают, когда number является более широким типом, чем 1?
x может быть слишком большим для сдвига 1 << x, приводящего к поведению undefined (UB). Даже если x не слишком велико, ~ может не переворачивать достаточно значительных бит.

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

Застраховать 1 достаточно широко:

В коде может использоваться 1ull или педантично (uintmax_t)1 и оптимизировать компилятор.

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

Или литье - это делает ошибки в кодировании/обзоре/обслуживании, сохраняя при этом правильность и актуальность.

number |= (type_of_number)1 << x;

Или мягко продвигайте 1, заставляя математическую операцию, которая меньше, чем тип number.

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

Как и большинство манипуляций с битами, лучше всего работать с неподписанными типами, а не с подписанными

3
ответ дан chux 27 сент. '17 в 21:18
источник поделиться

Для установки BitIdx -th бит в Number на BitValue

Number = Number xor (1 shl BitIdx) or (BitValue shl BitIdx)

Трюк здесь заключается в том, чтобы сначала безоговорочно очистить BitIdx -th бит, указав его с помощью 1. Эта версия выглядит несколько медленнее, чем с ветвлением (if bit = 1 then setbit else clearbit), но является однострочным.

0
ответ дан Fr0sT 22 мая '17 в 11:24
источник поделиться

Попробуйте одну из этих функций на языке C изменить n бит

char bitfield;

// start at 0th position

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}

или

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}

или

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;
}
-2
ответ дан Vincet 27 мая '14 в 14:46
источник поделиться

Другие вопросы по меткам