Как настроить, очистить и немного переключить?
Как вы устанавливаете, очищаете и переключаете один бит?
Ответы (27)
Установка немного
Используйте побитовый оператор ИЛИ (|
), чтобы установить бит.
number |= 1UL << n;
Это установит n
-й бит number
. n
должен быть равен нулю, если вы хотите установить 1
st бит и так далее до n-1
, если вы хотите установить n
th бит.
Используйте 1ULL
, если number
шире, чем unsigned long
; продвижение 1UL << n
не происходит до тех пор, пока не будет оценено 1UL << n
, где неопределенное поведение сдвигается более чем на ширину long
. То же самое относится ко всем остальным примерам.
Немного очищая
Используйте побитовый оператор И (&
), чтобы немного очистить.
number &= ~(1UL << n);
Это очистит n
-й бит number
. Вы должны инвертировать битовую строку с помощью побитового оператора НЕ (~
), а затем И.
Немного переключаюсь
Оператор XOR (^
) может использоваться для небольшого переключения.
number ^= 1UL << n;
Это переключит n
-й бит number
.
Немного проверяю
Вы не просили об этом, но я могу это добавить.
Чтобы немного проверить, сдвиньте число n вправо, затем побитовое И:
bit = (number >> n) & 1U;
Это поместит значение n
-го бита number
в переменную bit
.
Изменение n -го бита на x
Установка n
-го бита равным 1
или 0
может быть достигнута с помощью следующего в реализации C ++ с дополнением до 2:
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);
or
unsigned long newbit = !!x; // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);
Как правило, неплохо использовать беззнаковые типы для переносимых манипуляций с битами.
or
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n))
очистит n
-й бит, а (x << n)
установит n
-й бит в x
.
Также, как правило, неплохо не копировать / вставлять код в целом, и поэтому многие люди используют макросы препроцессора (например, ответ сообщества вики ниже) или инкапсуляция.
number
шире, чем int
- person anatolyg; 09.05.2013
bit = (number >> x) & 1
- person aaronman; 26.06.2013
1
- это int
литерал, который подписан. Таким образом, все операции здесь работают с числами со знаком, что не очень хорошо определено стандартами. Стандарты не гарантируют дополнение до двух или арифметический сдвиг, поэтому лучше использовать 1U
.
- person Siyuan Ren; 10.12.2013
unsigned long long
в любом случае ... могут быть расширения, определяемые реализацией, такие как __int128
. Чтобы быть сверхбезопасным (uintmax_t)1 << x
- person M.M; 07.02.2015
number = number & ~(1 << n) | (x << n);
для изменения n-го бита на x.
- person jiasli; 24.03.2015
number
больше int
и n
больше или равно количеству битов в int
. В этом случае он даже вызывает неопределенное поведение. number = number & ~((uintmax_t)1 << n) | ((uintmax_t)x << n);
- это общее выражение, которое должно работать для всех размеров number
, но может генерировать уродливый и неэффективный код для меньших размеров.
- person chqrlie; 24.03.2015
-x
, небезопасно, поскольку стандарт C позволяет целым числам со знаком быть (например) величиной знака, дополнением единиц или любой другой системой, которая может выражать необходимый диапазон, что означает, что -x
не должно быть таким же, как (~x) + 1
. Это не так уж важно для современных архитектур, но вы никогда не знаете, что достаточно умный оптимизирующий компилятор сделает с вашим кодом.
- person Kevin; 18.01.2016
~
вместо !
?
- person 71GA; 30.01.2017
~
) инвертирует все биты, поэтому ~0xFFF0FFFF
равно 0x000F0000
. Логическое не (!
) дает 0, если значение не равно нулю, или 1, если значение равно нулю, поэтому !0xFFF0FFFF
равно 0x00000000
.
- person Adrian McCarthy; 05.09.2017
-1
равно 0b1111..1110
. (Все единицы - это -0
). C ++ также допускает целочисленные представления знака / величины, где -x
просто переворачивает бит знака. Я обновил этот ответ, чтобы указать на это. Совершенно нормально, если вы ориентируетесь только на две дополнительные реализации C ++. Это не UB, это просто определяется реализацией, поэтому для правильной работы требуется определить целые числа со знаком как дополнение до 2. Я также изменил константы на 1UL
, отметив, что может потребоваться 1ULL
.
- person Peter Cordes; 10.11.2017
-x
- это UB, если x - INT_MIN.
- person Kevin; 10.11.2017
-(unsigned long)x
, что также обходило бы представление целых чисел со знаком. (беззнаковое основание 2 соответствует семантике дополнения до 2). Но оно работает правильно, только если x
равно 0 или 1. Помните, что мы устанавливаем бит n
на x
.
- person Peter Cordes; 11.11.2017
0U - 1U
- ›all-one. Как это выглядит сейчас? Я постарался, чтобы ответ был простым. Я уже упоминал, что вам может потребоваться логическое преобразование с помощью !!x
. Если бы это был мой собственный ответ, я мог бы вставить больше текста о том, чтобы всегда использовать беззнаковый, но я просто сохраняю этот старый канонический ответ. (Джереми, надеюсь, вам понравятся изменения, возможно, вы захотите отредактировать, чтобы изложить мои изменения своими словами или что-то еще, что вы хотите сказать почти 9 лет спустя.)
- person Peter Cordes; 11.11.2017
n
-го бита в раздел x
было добавлено другим сторонним редактированием и не было работой Джереми.
- person Peter Cordes; 11.11.2017
set bit to value
. Обычно я делаю save state
+ unconditionally set/clear
+ restore state
аппаратно. Во всяком случае, я случайно наткнулся на это и обязательно украду выражение выше.
- person sherrellbc; 15.01.2021
number = (number | (1UL << n)) ^ (!x << n)
Упрощено удаление логического и побитового не
- person Shogan Aversa-Druesne; 18.01.2021
x
. Не number = (number & ~(1UL << n)) | (x << n);
генерирует два битовых сдвига, один переворот байта, одно И, одно ИЛИ и, наконец, одно присваивание. В то время как if (x) { number |= (1 << n); } else { number &= ~(1 << n); }
генерирует одно сравнение, сдвиг на один бит, либо ИЛИ , или И с обращением байта, а также присваивание. 6 против 5 операций в худшем случае (сброс бита). 6 против 4 при установке бита. Намного более читабельным. Но, может быть, некоторые операции обходятся дороже?
- person Smartskaft2; 02.05.2021
x
, однако при рандомизации значения x не-if
-решение примерно вдвое быстрее, чем if
-решение: godbolt.org/z/5aEbKchzf Итак, да, вы правы, предсказание ветвления убивает производительность.
- person JulianH; 28.05.2021
Использование стандартной библиотеки C ++: std::bitset<N>
.
Свое катать не нужно:
#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 позволяет использовать битовый набор размера во время выполнения по сравнению с стандартной библиотекой размером во время компиляции. битсет.
sudo apt-get install boost-devel
- person Martin York; 13.08.2014
std::bitset
только здесь.
- person Martin York; 22.01.2020
Другой вариант - использовать битовые поля:
struct bits {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct bits mybits;
определяет 3-битное поле (фактически, это три 1-битных поля). Битовые операции теперь стали немного (хаха) проще:
Чтобы установить или сбросить бит:
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
Это работает только с битовыми полями фиксированного размера. В противном случае вам придется прибегнуть к технике бит-твиддлинга, описанной в предыдущих сообщениях.
sizeof(mybits)
я получаю 12 (т.е. размер трех ints
). Это место, выделенное в памяти, или какая-то ошибка в функции sizeof
?
- person iGbanam; 29.11.2012
gcc
версию 4.4.5
- person iGbanam; 29.11.2012
struct
можно поместить внутрь (обычно анонимного) union
с целым числом и т. Д. Это работает. (Я понимаю, что это старый тред, кстати)
- person Shade; 17.05.2014
__attribute__((packed))
- person ; 28.06.2020
Я использую макросы, определенные в файле заголовка, для обработки и сброса битов:
/* 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)))
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))
BITMASK_CHECK(x,y) ((x) & (y))
должен быть ((x) & (y)) == (y)
, иначе он вернет неверный результат по многобитной маске (например, 5
vs. 3
) / * Привет всем могильщикам :) * /
- person brigadir; 11.12.2014
1
должен быть (uintmax_t)1
или аналогичным на тот случай, если кто-то попытается использовать эти макросы в long
или более крупном типе
- person M.M; 07.02.2015
1ULL
работает так же хорошо, как (uintmax_t)
в большинстве реализаций.
- person Peter Cordes; 11.11.2017
BITMASK_CHECK_ALL(x,y)
может быть реализовано как !~((~(y))|(x))
- person Handy999; 20.11.2018
!(~(x) & (y))
- person Tavian Barnes; 13.08.2019
#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b)))) // '!!' to make sure this returns 0 or 1
- person William Martens; 16.02.2021
Иногда для имени битов стоит использовать enum
:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
Затем используйте имена позже. Т.е. записывать
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
установить, очистить и протестировать. Таким образом вы скрываете магические числа от остальной части вашего кода.
В остальном я поддерживаю решение Джереми.
clearbits()
вместо &= ~
. Почему вы используете для этого перечисление? Я думал, что это были для создания группы уникальных переменных со скрытым произвольным значением, но вы присваиваете определенное значение каждой из них. Так в чем же преимущество по сравнению с простым определением их как переменных?
- person endolith; 20.12.2011
enum
s для наборов связанных констант имеет долгую историю в программировании на языке C. Я подозреваю, что у современных компиляторов единственное преимущество перед const short
или чем-то еще заключается в том, что они явно сгруппированы вместе. И когда вы хотите, чтобы они были отличными от битовых масок, вы получаете автоматическую нумерацию. В С ++, конечно, они также образуют отдельные типы, что дает вам небольшую дополнительную статическую проверку ошибок.
- person dmckee --- ex-moderator kitten; 22.12.2011
enum ThingFlags
для ThingError|ThingFlag1
?
- person Luis Colorado; 30.09.2014
int
. Это может вызвать всевозможные тонкие ошибки из-за неявного целочисленного продвижения или побитовых операций над знаковыми типами. thingstate = ThingFlag1 >> 1
, например, вызовет поведение, определяемое реализацией. thingstate = (ThingFlag1 >> x) << y
может вызывать неопределенное поведение. И так далее. На всякий случай всегда приводите к беззнаковому типу.
- person Lundin; 14.12.2015
enum My16Bits: unsigned short { ... };
- person Aiken Drum; 15.03.2016
Из bitops.h snip-c.zip:
/*
** 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 того же выражения, которое обозначено оператором тильда (~).
Как только маска создана, она применяется к аргументу так, как вы предлагаете, с помощью побитовых операторов и (&), или (|) и 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
---- [финиш] ------------------------------------------- ----------------------
arg
равно long long
. 1L
должен быть максимально широким типом, поэтому (uintmax_t)1
. (Вам может сойти с рук 1ull
)
- person M.M; 07.02.2015
Для новичка я хотел бы немного подробнее объяснить на примере:
Пример:
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)
Поскольку это помечено как «встроенный», я предполагаю, что вы используете микроконтроллер. Все приведенные выше предложения действительны и работают (чтение-изменение-запись, объединения, структуры и т. Д.).
Однако во время отладки на основе осциллографа я был поражен, обнаружив, что эти методы имеют значительные накладные расходы в циклах ЦП по сравнению с записью значения непосредственно в регистры PORTnSET / PORTnCLEAR микроконтроллера, что дает реальную разницу там, где есть узкие петли / высокие значения. переключатели частоты ISR.
Для тех, кто не знаком: в моем примере микроконтроллер имеет общий регистр состояния контактов PORTn, который отражает выходные контакты, поэтому выполнение PORTn | = BIT_TO_SET приводит к чтению-изменению-записи в этот регистр. Однако регистры PORTnSET / PORTnCLEAR принимают «1», чтобы означать «пожалуйста, сделайте этот бит 1» (SET) или «пожалуйста, сделайте этот бит нулевым» (CLEAR) и «0», чтобы означать «оставьте вывод в покое». Таким образом, вы получаете два адреса порта в зависимости от того, устанавливаете ли вы бит или очищаете его (не всегда удобно), но намного быстрее реагируют и меньше собираемый код.
volatile
, и поэтому компилятор не может выполнять какие-либо оптимизации кода, включающего такие регистры. Поэтому рекомендуется разобрать такой код и посмотреть, как он получится на уровне ассемблера.
- person Lundin; 14.12.2015
Вот мой любимый макрос битовой арифметики, который работает с любыми типами целочисленных массивов без знака от 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, &)) ...
и т.п.
BITOP(array, bit++, |=);
в цикле, скорее всего, не приведет к тому, что хочет вызывающий.
- person foraidt; 13.07.2010
BITCELL(a,b) |= BITMASK(a,b);
(оба принимают a
в качестве аргумента для определения размера, но последний никогда не будет оцените a
, поскольку он появляется только в sizeof
).
- person R.. GitHub STOP HELPING ICE; 13.07.2010
(size_t)
приведение, кажется, существует только для того, чтобы подстраховаться от беззнаковой математики с %
. Мог (unsigned)
там.
- person chux - Reinstate Monica; 27.09.2017
(size_t)(b)/(8*sizeof *(a))
без необходимости может сузить b
перед разделением. Только проблема с очень большими битовыми массивами. Еще интересный макрос.
- person chux - Reinstate Monica; 27.09.2017
У подхода битового поля есть и другие преимущества во встраиваемой сфере. Вы можете определить структуру, которая отображается непосредственно на биты в конкретном аппаратном регистре.
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, но это может зависеть от реализации. Также проверьте, как ваш компилятор обрабатывает поля, пересекающие границы байтов.
Затем вы можете читать, писать, тестировать отдельные значения, как и раньше.
Проверить бит в произвольном месте в переменной произвольного типа:
#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; Я также тестировал его с помощью MSVC ++ 2008 на amd64. Можно сделать аналогичные макросы для установки и сброса битов. Ключевое отличие этого решения от многих других здесь заключается в том, что оно работает для любого местоположения практически с любым типом переменных.
В более общем случае для растровых изображений произвольного размера:
#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)))
CHAR_BIT
уже определен limits.h
, вам не нужно вставлять свой собственный BITS
(и на самом деле вы сделаете свой код хуже, сделав это)
- person M.M; 07.02.2015
Эта программа предназначена для изменения любого бита данных с 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);
}
}
}
Использовать этот:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
Если вы много занимаетесь тиддлом, возможно, вы захотите использовать маски, которые сделают все это быстрее. Следующие функции очень быстрые и по-прежнему гибкие (они позволяют использовать битовые карты любого размера).
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);
Вы должны убедиться, что номер бита находится в диапазоне передаваемой битовой карты. Обратите внимание, что для процессоров с прямым порядком байтов, которые байты, слова, двойные слова, qwords и т. Д. Правильно сопоставляются друг с другом в памяти (основная причина того, что процессоры с прямым порядком байтов `` лучше '', чем процессоры с прямым порядком байтов, я чувствую, что приближается война пламени на...).
Расширяя ответ 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;
}
Давайте сначала предположим несколько вещей
num = 55
Целое число для выполнения побитовых операций (установка, получение, очистка, переключение).n = 4
0 на основе позиции разряда для выполнения побитовых операций.
Как получить немного?
- Чтобы получить
nth
бит числа, сдвиньте вправоnum
,n
раз. Затем выполните побитовое И&
с 1.
bit = (num >> n) & 1;
Как это работает?
0011 0111 (55 in decimal)
>> 4 (right shift 4 times)
-----------------
0000 0011
& 0000 0001 (1 in decimal)
-----------------
=> 0000 0001 (final result)
Как установить немного?
- Чтобы установить конкретный бит числа. Сдвиг влево 1
n
раз. Затем выполните побитовую операцию ИЛИ|
сnum
.
num |= (1 << n); // Equivalent to; num = (1 << n) | num;
Как это работает?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
| 0011 0111 (55 in decimal)
-----------------
=> 0001 0000 (final result)
Как немного очистить?
- Левый сдвиг 1,
n
раз, т.е.1 << n
. - Выполните побитовое дополнение с полученным выше результатом. Таким образом, n-й бит становится не установленным, а остальной бит становится установленным, то есть
~ (1 << n)
. - Наконец, выполните побитовую операцию И
&
с результатом выше иnum
. Вышеупомянутые три шага вместе можно записать какnum & (~ (1 << n))
;
num &= (~(1 << n)); // Equivalent to; num = num & (~(1 << n));
Как это работает?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
~ 0001 0000
-----------------
1110 1111
& 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
Как немного переключить?
Чтобы немного переключиться, мы используем побитовый оператор XOR ^
. Побитовый оператор XOR оценивается как 1, если соответствующие биты обоих операндов различны, в противном случае оценивается как 0.
Это означает, что для небольшого переключения нам нужно выполнить операцию XOR с битом, который вы хотите переключить, и 1.
num ^= (1 << n); // Equivalent to; num = num ^ (1 << n);
Как это работает?
- Если бит для переключения равен 0, тогда
0 ^ 1 => 1
. - Если бит для переключения равен 1, то
1 ^ 1 => 0
.
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
^ 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
Рекомендуемая литература - Упражнения для побитовых операторов
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
упростите установку и очистку bool.
Чтобы найти непечатаемые символы:
// 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, используя шестизначный номер ссуды в качестве индекса в битовом массиве. Очень быстро и через 8 месяцев доказало, что система мэйнфрейма, из которой мы получали данные, на самом деле неисправна. Простота битовых массивов делает очень высокую уверенность в их правильности - например, по сравнению с поисковым подходом.
bool
. Возможно, даже 4 байта для настроек C89, которые используют int
для реализации bool
- person M.M; 07.02.2015
Если вы хотите выполнить всю эту операцию с программированием на 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 и полезны для обеспечения согласованности между процессорами.
Вот несколько макросов, которые я использую:
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)
Как вы устанавливаете, очищаете и переключаете один бит?
Чтобы устранить распространенную ошибку кодирования при попытке сформировать маску: 1
не всегда достаточно широкое
Какие проблемы возникают, когда number
является более широким типом, чем 1
? x
может быть слишком большим для сдвига 1 << x
, ведущего к неопределенному поведению (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;
Как и в случае с большинством битовых манипуляций, лучше всего работать с типами без знака, а не с знаковыми.
number |= (type_of_number)1 << x;
, ни number |= (number*0 + 1) << x;
не подходят для установки бит знака подписанного типа ... Фактически, ни то, ни другое не является number |= (1ull << x);
. Есть ли портативный способ сделать это по позиции?
- person chqrlie; 28.09.2017
Эта программа основана на вышеупомянутом решении @ Jeremy. Если кто хочет поскорее поиграться.
public class BitwiseOperations {
public static void main(String args[]) {
setABit(0, 4); // set the 4th bit, 0000 -> 1000 [8]
clearABit(16, 5); // clear the 5th bit, 10000 -> 00000 [0]
toggleABit(8, 4); // toggle the 4th bit, 1000 -> 0000 [0]
checkABit(8,4); // check the 4th bit 1000 -> true
}
public static void setABit(int input, int n) {
input = input | ( 1 << n-1);
System.out.println(input);
}
public static void clearABit(int input, int n) {
input = input & ~(1 << n-1);
System.out.println(input);
}
public static void toggleABit(int input, int n) {
input = input ^ (1 << n-1);
System.out.println(input);
}
public static void checkABit(int input, int n) {
boolean isSet = ((input >> n-1) & 1) == 1;
System.out.println(isSet);
}
}
Output :
8
0
0
true
Шаблонная версия (помещена в файл заголовка) с поддержкой изменения нескольких бит (работает на микроконтроллерах AVR, кстати):
namespace bit {
template <typename T1, typename T2>
constexpr inline T1 bitmask(T2 bit)
{return (T1)1 << bit;}
template <typename T1, typename T3, typename ...T2>
constexpr inline T1 bitmask(T3 bit, T2 ...bits)
{return ((T1)1 << bit) | bitmask<T1>(bits...);}
/** Set these bits (others retain their state) */
template <typename T1, typename ...T2>
constexpr inline void set (T1 &variable, T2 ...bits)
{variable |= bitmask<T1>(bits...);}
/** Set only these bits (others will be cleared) */
template <typename T1, typename ...T2>
constexpr inline void setOnly (T1 &variable, T2 ...bits)
{variable = bitmask<T1>(bits...);}
/** Clear these bits (others retain their state) */
template <typename T1, typename ...T2>
constexpr inline void clear (T1 &variable, T2 ...bits)
{variable &= ~bitmask<T1>(bits...);}
/** Flip these bits (others retain their state) */
template <typename T1, typename ...T2>
constexpr inline void flip (T1 &variable, T2 ...bits)
{variable ^= bitmask<T1>(bits...);}
/** Check if any of these bits are set */
template <typename T1, typename ...T2>
constexpr inline bool isAnySet(const T1 &variable, T2 ...bits)
{return variable & bitmask<T1>(bits...);}
/** Check if all these bits are set */
template <typename T1, typename ...T2>
constexpr inline bool isSet (const T1 &variable, T2 ...bits)
{return ((variable & bitmask<T1>(bits...)) == bitmask<T1>(bits...));}
/** Check if all these bits are not set */
template <typename T1, typename ...T2>
constexpr inline bool isNotSet (const T1 &variable, T2 ...bits)
{return ((variable & bitmask<T1>(bits...)) != bitmask<T1>(bits...));}
}
Пример использования:
#include <iostream>
#include <bitset> // for console output of binary values
// and include the code above of course
using namespace std;
int main() {
uint8_t v = 0b1111'1100;
bit::set(v, 0);
cout << bitset<8>(v) << endl;
bit::clear(v, 0,1);
cout << bitset<8>(v) << endl;
bit::flip(v, 0,1);
cout << bitset<8>(v) << endl;
bit::clear(v, 0,1,2,3,4,5,6,7);
cout << bitset<8>(v) << endl;
bit::flip(v, 0,7);
cout << bitset<8>(v) << endl;
}
Кстати: оказывается, что constexpr и inline не используются, если не отправлять аргумент оптимизатора (например: -O3) компилятору. Не стесняйтесь попробовать код на https://godbolt.org/ и посмотрите на вывод ASM.
;
после определений функций?)
- person melpomene; 10.02.2018
((variable & bits) == bits)
- person Joakim L. Christiansen; 27.02.2018
std::bitset
in c ++ 11
- person pqnet; 25.10.2019
Установка n-го бита в x (значение бита) без использования -1
Иногда, когда вы не уверены, к чему приведет -1 или что-то подобное, вы можете установить n-й бит без использования -1:
number = (((number | (1 << n)) ^ (1 << n))) | (x << n);
Объяснение: ((number | (1 << n)
устанавливает n-й бит в 1 (где |
означает побитовое ИЛИ), затем с помощью (...) ^ (1 << n)
мы устанавливаем n-й бит в 0 и, наконец, с помощью (...) | x << n)
мы устанавливаем n-й бит, который был равен 0, в (значение бита) x
.
Это также работает в golang
.
(number & ~(1 << n)) | (!!x << n)
.
- person Will Eccles; 08.05.2021
Вот процедура на C для выполнения основных побитовых операций:
#define INT_BIT (unsigned int) (sizeof(unsigned int) * 8U) //number of bits in unsigned int
int main(void)
{
unsigned int k = 5; //k is the bit position; here it is the 5th bit from the LSb (0th bit)
unsigned int regA = 0x00007C7C; //we perform bitwise operations on regA
regA |= (1U << k); //Set kth bit
regA &= ~(1U << k); //Clear kth bit
regA ^= (1U << k); //Toggle kth bit
regA = (regA << k) | regA >> (INT_BIT - k); //Rotate left by k bits
regA = (regA >> k) | regA << (INT_BIT - k); //Rotate right by k bits
return 0;
}
Попробуйте одну из этих функций на языке C, чтобы изменить n бит:
char bitfield;
// Start at 0th position
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}
Or
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}
Or
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;
}
value << n
может вызвать неопределенное поведение
- person M.M; 07.02.2015
1
на 0x1
или 1UL
, чтобы не говорить о UB @ M.M
- person KPCT; 18.04.2021
check_nth_bit
может быть bool
.
- person Xeverous; 05.05.2020