Как настроить, очистить и немного переключить?
Вопрос
Как вы устанавливаете, очищаете и переключаете один бит?
- прочтите это: graphics.stanford.edu/~seander/bithacks.html и, когда вы освоите это, прочтите это: realtimecollisiondetection.net/blog/?p=78 ugasoft 18.09.2008
- Вам также может быть интересно попробовать The Bit Twiddler, Bit Twiddling Hacks и Агрегированные магические алгоритмы. 05.01.2009
Ответы
Ответ 1 · рейтинг 3923 · Paige Ruten
Установка немного
Используйте побитовый оператор ИЛИ (|), чтобы установить бит.
number |= 1UL << n;
Это установит n-й бит number. n должен быть равен нулю, если вы хотите установить 1st бит и так далее до n-1, если вы хотите установить nth бит.
Используйте 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.
Также, как правило, неплохо не копировать / вставлять код в целом, и поэтому многие люди используют макросы препроцессора (например, ответ сообщества вики ниже) или инкапсуляция.
Ответ 2 · рейтинг 494 · Martin York
Использование стандартной библиотеки 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 позволяет использовать битовый набор размера во время выполнения по сравнению с стандартной библиотекой размером во время компиляции. битсет.
Ответ 3 · рейтинг 263 · Ferruccio
Другой вариант - использовать битовые поля:
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
Это работает только с битовыми полями фиксированного размера. В противном случае вам придется прибегнуть к технике бит-твиддлинга, описанной в предыдущих сообщениях.
Ответ 4 · рейтинг 207 · Community
Я использую макросы, определенные в файле заголовка, для обработки и сброса битов:
/* 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))
Ответ 5 · рейтинг 128 · dmckee --- ex-moderator kitten
Иногда для имени битов стоит использовать enum:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
Затем используйте имена позже. Т.е. записывать
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
установить, очистить и протестировать. Таким образом вы скрываете магические числа от остальной части вашего кода.
В остальном я поддерживаю решение Джереми.
Ответ 6 · рейтинг 52 · yogeesh
Из 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
---- [финиш] ------------------------------------------- ----------------------
Ответ 7 · рейтинг 36 · kapilddit
Для новичка я хотел бы немного подробнее объяснить на примере:
Пример:
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)
Ответ 8 · рейтинг 27 · John U
Поскольку это помечено как "встроенный", я предполагаю, что вы используете микроконтроллер. Все приведенные выше предложения действительны и работают (чтение-изменение-запись, объединения, структуры и т. Д.).
Однако во время отладки на основе осциллографа я был поражен, обнаружив, что эти методы имеют значительные накладные расходы в циклах ЦП по сравнению с записью значения непосредственно в регистры PORTnSET / PORTnCLEAR микроконтроллера, что дает реальную разницу там, где есть узкие петли / высокие значения. переключатели частоты ISR.
Для тех, кто не знаком: в моем примере микроконтроллер имеет общий регистр состояния контактов PORTn, который отражает выходные контакты, поэтому выполнение PORTn | = BIT_TO_SET приводит к чтению-изменению-записи в этот регистр. Однако регистры PORTnSET / PORTnCLEAR принимают "1", чтобы означать "пожалуйста, сделайте этот бит 1" (SET) или "пожалуйста, сделайте этот бит нулевым" (CLEAR) и "0", чтобы означать "оставьте вывод в покое". Таким образом, вы получаете два адреса порта в зависимости от того, устанавливаете ли вы бит или очищаете его (не всегда удобно), но намного быстрее реагируют и меньше собираемый код.
Ответ 9 · рейтинг 27 · R.. GitHub STOP HELPING ICE
Вот мой любимый макрос битовой арифметики, который работает с любыми типами целочисленных массивов без знака от 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, &)) ...
и т.п.
Ответ 10 · рейтинг 25 · Roddy
У подхода битового поля есть и другие преимущества во встраиваемой сфере. Вы можете определить структуру, которая отображается непосредственно на биты в конкретном аппаратном регистре.
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, но это может зависеть от реализации. Также проверьте, как ваш компилятор обрабатывает поля, пересекающие границы байтов.
Затем вы можете читать, писать, тестировать отдельные значения, как и раньше.
Ответ 11 · рейтинг 20 · John Zwinck
Проверить бит в произвольном месте в переменной произвольного типа
#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. Можно сделать аналогичные макросы для установки и сброса битов. Ключевое отличие этого решения от многих других здесь заключается в том, что оно работает для любого местоположения практически с любым типом переменных.
Ответ 12 · рейтинг 20 · bill
В более общем случае для растровых изображений произвольного размера:
#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)))
Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.
Похожие материалы
- .NET CORE 3.1 Web API для начинающих, часть 02 - Просмотр файлов проекта.
- Генератор паролей на C
- Базовый синтаксис С++
- Учебное руководство - Как начать программировать на C ++
- Продлевают ли константные ссылки в структурированных привязках время жизни разложенного объекта?
- Dynamic_cast в утверждении, вызывающем ошибку