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

2320

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

  • 45
    прочитайте это: graphics.stanford.edu/~seander/bithacks.html и, когда вы освоите это, прочитайте это: realtimecollisiondetection.net/blog/?p=78
  • 5
    Вы также можете быть заинтересованы в проверке из битового Twiddler , с битами Хаки и агрегированные Волшебные алгоритмами .
Показать ещё 5 комментариев
Теги:
bit-manipulation
bitwise-operators

27 ответов

3179
Лучший ответ

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

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

number |= 1UL << n;

Это установит n й бит number. n должно быть нулевым, если вы хотите установить 1 й бит и так далее до n-1, если вы хотите установить n й бит.

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

Очистка бит

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

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

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

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

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

number ^= 1UL << n;

Это будет переключать n й бит number.

Проверка бит

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

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

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

Это поместит значение n го бита number в bit переменной.

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

Установка n го бита на 1 или 0 может быть достигнута с помощью следующей реализации 2 дополнения 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);

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

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

  • 110
    Я хотел бы отметить, что на платформах, которые имеют встроенную поддержку установки / сброса битов (например, микроконтроллеры AVR), компиляторы часто переводят «myByte | = (1 << x)» в собственные инструкции установки / сброса битов, когда x равен константа, например: (1 << 5) или const без знака x = 5.
  • 47
    бит = число & (1 << x); не будет помещать значение бита x в бит, если только бит не имеет тип _Bool (<stdbool.h>). В противном случае, бит = !! (число & (1 << x)); будут..
Показать ещё 26 комментариев
425

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

  • 27
    +1. Не то, чтобы std :: bitset можно было использовать из "C", но, поскольку автор отметил свой вопрос как "C ++", AFAIK, ваш ответ здесь лучший ... std :: vector <bool> - это другой способ, если знать его плюсы и минусы
  • 1
    Хороший, Мартин! Вы даже можете использовать enum для «индексации» битов: enum {cEngineOn, cDoorsOpen, cAircoOn}; std :: bitset <cNBBITS> mybits; mybits [cEngineOn] .set (); const bool cbDoorOpen = mybits [cDoorsOpen]; ...
Показать ещё 17 комментариев
213

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

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

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

  • 0
    Битовое поле члена объявления?
  • 60
    Я всегда считал использование битовых полей плохой идеей. У вас нет контроля над порядком, в котором распределяются биты (сверху или снизу), что делает невозможным сериализацию значения стабильным / переносным способом, кроме как по битам. Также невозможно смешать битовую арифметику DIY с битовыми полями, например, создать маску, которая проверяет несколько битов одновременно. Конечно, вы можете использовать && и надеяться, что компилятор оптимизирует его правильно ...
Показать ещё 10 комментариев
144

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

/* 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))
  • 17
    Э-э, я понимаю, что это 5-летняя запись, но ни в одном из этих макросов нет дублирования аргументов, Дэн
  • 11
    BITMASK_CHECK(x,y) ((x) & (y)) должно быть ((x) & (y)) == (y) противном случае он возвращает неверный результат для многобитовой маски (например, 5 против 3 ) / * Hello всем могильщикам:) * /
Показать ещё 4 комментария
111

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

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

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

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

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

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

  • 1
    В качестве альтернативы вы можете сделать функцию clearbits() вместо &= ~ . Почему вы используете enum для этого? Я думал, что они предназначены для создания набора уникальных переменных со скрытым произвольным значением, но вы назначаете определенное значение каждой из них. Так в чем же выгода от определения их как переменных?
  • 3
    @endolith: использование enum s для наборов связанных констант имеет большое значение в программировании на c. Я подозреваю, что у современных компиляторов единственным преимуществом по сравнению с const short или чем-то еще является то, что они явно сгруппированы вместе. И когда вы хотите , чтобы они что - то другое , чем битмаски вы получаете автоматическую нумерацию. В C ++, конечно, они также формируют различные типы, что дает вам немного больше статической проверки ошибок.
Показать ещё 3 комментария
42

От snip-c.zip 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)))

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

Общее выражение, с которым у вас возникли проблемы во всех этих случаях, - "(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, short, int, или long.

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

Убежденный? Здесь некоторый тестовый код - я использовал 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] ------------------------------------------- ----------------------

  • 3
    2 вещи об этом: (1) при просмотре ваших макросов некоторые могут ошибочно полагать, что макросы действительно устанавливают / сбрасывают / переворачивают биты в аргументе, однако присваивания нет; (2) ваш test.c не завершен; Я подозреваю, что если вы запустите больше дел, вы найдете проблему (упражнение для чтения)
  • 17
    -1 Это просто странное запутывание. Никогда не изобретайте язык C, скрывая синтаксис языка за макросами, это очень плохая практика. Затем некоторые странности: сначала подписывается 1L, что означает, что все битовые операции будут выполняться со знаком. Все, что передано этим макросам, будет возвращено как подписанное долго. Нехорошо. Во-вторых, это будет очень неэффективно работать на меньших процессорах, так как оно будет работать долго, когда операции могли выполняться на уровне int. В-третьих, функционально-подобные макросы являются корнем всего зла: у вас нет никакой безопасности типов. Кроме того, предыдущий комментарий об отсутствии назначения очень действителен.
Показать ещё 2 комментария
32

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

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

foo = foo | 001b

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

if ( foo & 001b ) ....

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

foo = foo & 110b

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

  • 3
    В C. нет двоичных обозначений. Двоичные целочисленные константы являются нестандартным расширением.
31

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

Пример:

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

Здесь мой любимый битовый арифметический макрос, который работает для любого типа беззнакового целочисленного массива от 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, &)) ...

и др.

  • 4
    Это хорошо читать, но нужно знать о возможных побочных эффектах. Использование BITOP(array, bit++, |=); в цикле, скорее всего, не будет делать то, что хочет вызывающий.
  • 0
    В самом деле. =) Один вариант, который вы можете предпочесть, состоит в том, чтобы разделить его на 2 макроса, 1 для адресации элемента массива, а другой для сдвига бита на место, ala BITCELL(a,b) |= BITMASK(a,b); (оба принимают в качестве аргумента , чтобы определить размер, но последний никогда не будет оценивать , так как он появляется только в a a sizeof ).
Показать ещё 3 комментария
24

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

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

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

  • 0
    Микро был Coldfire MCF52259, используя C в Codewarrior. Рассмотрение дизассемблера / ассемблера является полезным упражнением, поскольку оно показывает все шаги, которые ЦП должен пройти, чтобы выполнить даже самые основные операции. <br> Мы также заметили другие инструкции по переключению ЦП в циклы, критичные ко времени - ограничение переменной путем выполнения var% = max_val стоит кучу циклов ЦП каждый раз, в то время как выполняется, если (var> max_val) var- = max_val использует только пара инструкций. <br> Хорошее руководство по нескольким трюкам здесь: codeproject.com/Articles/6154/…
  • 0
    Еще важнее то, что регистры ввода-вывода с отображением вспомогательной памяти обеспечивают механизм атомарных обновлений. Чтение / изменение / запись могут пойти очень плохо, если последовательность прервана.
Показать ещё 1 комментарий
21

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

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, но это может быть зависимым от реализации. Кроме того, проверьте, как поля обработчиков ваших обработчиков пересекают границы байтов.

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

  • 2
    Практически все, что касается битовых полей, определяется реализацией. Даже если вам удастся выяснить все детали относительно того, как ваш конкретный компилятор реализует их, использование их в вашем коде наверняка сделает его непереносимым.
  • 1
    @Lundin - Правда, но битовая встроенная система (особенно в аппаратных регистрах, к которой относится мой ответ) никогда не будет полезной в любом случае переносимой.
Показать ещё 2 комментария
19

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

#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)))
  • 2
    CHAR_BIT уже определен limits.h , вам не нужно вставлять свои собственные BITS (и фактически вы делаете свой код хуже, делая это)
18

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

#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. Можно создать аналогичные макросы для установки и очистки бит. Ключевое отличие этого решения по сравнению со многими другими здесь заключается в том, что он работает для любого местоположения практически в любом типе переменных.

14

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

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, которые байты, слова, слова, слова и т.д. Правильно отображают друг друга в памяти (основная причина, по которой маленькие процессоры endian лучше, чем процессоры большого числа, ах, я чувствую, что начинается пламенная война на...).

  • 2
    Не используйте таблицу для функции, которая может быть реализована одним оператором. TQuickByteMask [n] эквивалентен (1 << n). Кроме того, короткая аргументация - очень плохая идея. На самом деле / и% будет делением, а не битовым сдвигом / побитовым, и потому что знаковое деление со степенью 2 не может быть реализовано побитовым. Вы должны сделать тип аргумента unsigned int!
  • 0
    Какой в этом смысл? Это только делает код медленнее и сложнее для чтения? Я не вижу в этом ни одного преимущества. 1u << n легче читать для программистов на C, и, мы надеемся, может быть преобразовано в инструкцию CPU с одним тактом. С другой стороны, ваше деление будет переведено примерно в 10 тиков, или даже так плохо, как до 100 тиков, в зависимости от того, насколько плохо конкретная архитектура справляется с делением. Что касается функции растрового изображения, то было бы более разумно иметь справочную таблицу, переводящую каждый битовый индекс в байтовый индекс, чтобы оптимизировать скорость.
Показать ещё 2 комментария
13

Эта программа предназначена для изменения любого бита данных от 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);
        }
    }
}
11

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

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

    return num;
}
  • 5
    Ну, это использует неэффективное ветвление.
  • 3
    @asdf Задача компилятора - вывести наиболее эффективный двоичный файл, задача программиста - написать чистый код
Показать ещё 1 комментарий
10

Если вы хотите выполнить эту операцию с программированием на языке C в ядре Linux, я предлагаю использовать стандартные API ядра 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-компьютерах и полезны для обеспечения согласованности между процессорами.

10

Расширение ответа 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;
}
9

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 месяцев доказал, что система мейнфреймов, из которой мы получали данные, фактически неисправна. Простота бит-массивов делает уверенность в их правильности очень высокой - например, под поисковым подходом.

  • 0
    std :: bitset действительно реализован в виде битов большинством компиляторов
  • 0
    @galinette, Согласен. Заголовочный файл #include <bitset> является хорошим ресурсом в этом отношении. Кроме того, специальный класс vector <bool> для случаев, когда вам нужно изменить размер вектора. C ++ STL, 2nd Edition, Николай М. Йосуттис подробно описывает их на страницах 650 и 281 соответственно. C ++ 11 добавляет несколько новых возможностей в std :: bitset, особый интерес для меня представляет хеш-функция в неупорядоченных контейнерах. Спасибо за головы! Я собираюсь удалить мой комментарий о судорогах. Уже достаточно мусора в сети. Я не хочу добавлять к этому.
Показать ещё 3 комментария
6

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

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

  • 2
    0x - это префикс для литерала в шестнадцатеричном, а не двоичном формате.
5

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

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

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

Чтобы решить проблему с общей ошибкой кодирования при попытке сформировать маску:
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;

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

  • 0
    Интересный взгляд на старый вопрос! Ни number |= (type_of_number)1 << x; ни number |= (number*0 + 1) << x; уместно установить бит знака знакового типа ... На самом деле, также не number |= (1ull << x); , Есть ли портативный способ сделать это по позиции?
  • 1
    @chqrlie IMO, лучший способ избежать установки знакового бита и риска UB или IDB со сдвигами - это использовать неподписанные типы. Очень переносимый код со сдвигом и подписью слишком сложен, чтобы быть приемлемым
3

Используемая переменная

int value, pos;

value - Данные
pos - позиция бит, который мы хотим установить, очистить или переключить
Установите немного

value = value | 1 << pos;

Очистить бит

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

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

value = value ^ 1 << pos;
3
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);
}
3

Шаблонная версия С++ 11 (помещается в заголовок):

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;}
}
  • 0
    Этот код не работает. (Кроме того, почему у вас есть ; после определения вашей функции?)
  • 0
    @ melpomene Код не сломан, я его проверил. Ты имеешь в виду, что он не скомпилируется или результат неверный? О лишних ';' Я не помню, они действительно могут быть удалены.
Показать ещё 2 комментария
-1

Попробуйте одну из этих функций на языке 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;
}
  • 0
    value << n может вызвать неопределенное поведение
-3

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню