Как вы устанавливаете, очищаете и немного переключаетесь?
Установка немного
Используйте побитовый оператор ИЛИ ( |
), чтобы установить бит.
number |= 1UL << n;
Это будет n
немного number
. n
должен быть равен нулю, если вы хотите установить 1
бит st и так далее n-1
, если вы хотите установить n
бит th.
Используйте 1ULL
если number
шире, чем unsigned long
; продвижение 1UL << n
не происходит до тех пор, пока после оценки того, 1UL << n
где его неопределенное поведение смещается более чем на ширину a long
. То же самое относится ко всем остальным примерам.
Прояснение немного
Используйте побитовый оператор AND ( &
), чтобы очистить немного.
number &= ~(1UL << n);
Это очистит n
й бит number
. Вы должны инвертировать битовую строку с помощью побитового оператора NOT ( ~
), затем AND it.
Немного тумблер
Оператор XOR ( ^
) может быть использован для переключения немного.
number ^= 1UL << n;
Это будет переключать n
й бит number
.
Немного проверяя
Вы не просили об этом, но я мог бы также добавить это.
Чтобы проверить немного, сдвиньте число n вправо, затем поразрядно И это:
bit = (number >> n) & 1U;
Это поместит значение n
th-го бита number
в переменную bit
.
Изменение n- го бита на x
Установка для n
бита th либо одного, 1
либо 0
может быть достигнута с помощью следующего в реализации C ++ дополнения 2:
number ^= (-x ^ number) & (1UL << n);
Бит n
будет установлен, если x
есть 1
, и очищен, если x
есть 0
. Если x
имеет какое-то другое значение, вы получите мусор. x = !!x
будет логизировать его до 0 или 1.
Чтобы сделать это независимым от поведения отрицания дополнения 2 (где -1
установлены все биты, в отличие от реализации C ++ для дополнения 1 или знака / величины), используйте отрицание без знака.
number ^= (-(unsigned long)x ^ number) & (1UL << n);
или
unsigned long newbit = !!x; // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);
Как правило, рекомендуется использовать типы без знака для переносимых операций с битами.
или
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n))
очистит n
бит th и (x << n)
установит n
бит th в x
.
Также, в целом, хорошая идея - не копировать / вставлять код в целом, и поэтому многие люди используют макросы препроцессора (как вики-сообщество ответит ниже ) или инкапсуляцию.
bit = (number >> x) & 1
1
это int
литерал, который подписан. Таким образом, все операции здесь выполняются на подписанных номерах, что не очень хорошо определено стандартами. Стандарты не гарантируют двоичного дополнения или арифметического сдвига, поэтому его лучше использовать 1U
. number = number & ~(1 << n) | (x << n);
менять n-й бит на x. Используйте побитовые операторы: &
|
Чтобы установить последний бит в 000b
:
foo = foo | 001b
Чтобы проверить последний бит в foo
:
if ( foo & 001b ) ....
Чтобы очистить последний бит в foo
:
foo = foo & 110b
Я использовал XXXb
для ясности. Вы, вероятно, будете работать с HEX-представлением, в зависимости от структуры данных, в которую вы упаковываете биты.
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
Иногда стоит использовать enum
для именования биты:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
Затем используйте имена позже. То есть пиши
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
установить, очистить и проверить. Таким образом вы скрываете магические числа от остальной части вашего кода.
Кроме этого я одобряю решение Джереми.
clearbits()
функцию вместо &= ~
. Почему вы используете перечисление для этого? Я думал, что они предназначены для создания набора уникальных переменных со скрытым произвольным значением, но вы назначаете определенное значение каждой из них. Так в чем же выгода от определения их как переменных? enum
s для наборов связанных констант имеет большое значение в программировании на c. Я подозреваю, что с современными компиляторами единственное преимущество по сравнению с const short
тем, что они явно сгруппированы вместе. И когда вы хотите , чтобы они что - то другое , чем битмаски вы получаете автоматическую нумерацию. В C ++, конечно, они также формируют различные типы, что дает вам немного больше статической проверки ошибок. enum ThingFlags
значение для ThingError|ThingFlag1
, например? int
. Это может вызывать любые тонкие ошибки из-за неявного целочисленного продвижения или побитовых операций со знаковыми типами. thingstate = ThingFlag1 >> 1
будет, например, вызывать поведение, определяемое реализацией. thingstate = (ThingFlag1 >> x) << y
может вызвать неопределенное поведение. И так далее. Чтобы быть в безопасности, всегда приводите к типу без знака. enum My16Bits: unsigned short { ... };
Другой вариант - использовать битовые поля:
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
Это работает только с битовыми полями фиксированного размера. В противном случае вам придется прибегнуть к методам битового поворота, описанным в предыдущих постах.
Из snop-c.zip 's bitops.h:
/*
** Bit set, clear, and test operations
**
** public domain snippet by Bob Stout
*/
typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;
#define BOOL(x) (!(!(x)))
#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))
Хорошо, давайте проанализируем вещи ...
Общее выражение, с которым у вас, похоже, возникают проблемы, - это "(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] ------------------------------------------- ----------------------
arg
есть long long
. 1L
должен быть максимально широкого типа, так (uintmax_t)1
. (Вы можете сойти с рук 1ull
)Если вы делаете много трюков, вы можете использовать маски, которые сделают все это быстрее. Следующие функции очень быстрые и гибкие (они позволяют переворачивать биты в битовых картах любого размера).
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);
Это зависит от вас, чтобы убедиться, что номер бита находится в пределах диапазона битовой карты, которую вы передаете. Обратите внимание, что для процессоров с прямым порядком байтов байты, слова, слова, слова и т. Д. Правильно отображаются в памяти друг друга (основная причина, по которой процессоры с прямым порядком байтов 'лучше', чем процессоры с прямым порядком байтов, ах, я чувствую грядущую войну пламени на...).
Использование стандартной библиотеки 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 допускает размер набора битов времени выполнения по сравнению со стандартным набором битов размера библиотеки .
Я использую макросы, определенные в заголовочном файле, для обработки установленных битов и очистки:
/* 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))
BITMASK_CHECK(x,y) ((x) & (y))
в ((x) & (y)) == (y)
противном случае он возвращает неверный результат для многобитовой маски (например, 5
против 3
) / * Привет всем могильщикам :) * /1
должно быть (uintmax_t)1
или похоже на случай, если кто-то попытается использовать эти макросы для более long
крупного типаBITMASK_CHECK_ALL(x,y)
может быть реализовано как!~((~(y))|(x))
!(~(x) & (y))
Подход битового поля имеет другие преимущества во встроенной области. Вы можете определить структуру, которая отображается непосредственно на биты в конкретном аппаратном регистре.
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. Можно сделать аналогичные макросы для установки и очистки битов. Ключевое отличие этого решения по сравнению со многими другими здесь заключается в том, что оно работает для любого местоположения практически во всех типах переменных.
Использовать это:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
В целом, для растровых изображений произвольного размера:
#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
(и фактически вы делаете свой код хуже, делая это)Вот мой любимый битовый арифметический макрос, который работает для любого типа целочисленного массива без знака от 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++, |=);
в цикле, скорее всего, не будет делать то, что хочет вызывающий. BITCELL(a,b) |= BITMASK(a,b);
(оба принимают a
в качестве аргумента для определения размера, но последний никогда не будет вычислять, a
так как это появляется только в sizeof
). (size_t)
бросок , кажется, есть только обеспечить некоторую беззнаковую математику с %
. Мог (unsigned)
там. (size_t)(b)/(8*sizeof *(a))
Ненужно может быть сужена b
до разделения. Только проблема с очень большими битовыми массивами. Все еще интересный макрос. Эта программа должна изменить любой бит данных с 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 x = x | 0x?;
где ?
позиция бита в двоичной форме.
0x
это префикс для литерала в шестнадцатеричном, а не двоичном. Для новичка я хотел бы объяснить немного больше с примером:
Пример:
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
Для тех, кто незнаком: в моем примере у micro есть общий регистр состояния выводов PORTn, который отражает выходные выводы, поэтому выполнение PORTn | = BIT_TO_SET приводит к чтению-модификации-записи в этот регистр. Однако регистры PORTnSET / PORTnCLEAR принимают «1» для обозначения «пожалуйста, сделайте этот бит 1» (SET) или «пожалуйста, сделайте этот бит нулевым» (CLEAR) и «0» для «оставьте пин-код в покое». Таким образом, вы получаете два адреса портов, в зависимости от того, устанавливаете ли вы или очищаете бит (не всегда удобно), но гораздо более быстрая реакция и меньший собранный код.
volatile
и, следовательно, компилятор не сможет выполнить какую-либо оптимизацию кода, включающего такие регистры. Поэтому хорошей практикой является дизассемблирование такого кода и просмотр его результатов на уровне ассемблера. 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 значения.
Однажды я использовал этот подход, чтобы найти дубликаты записей ссуды, где loan_number был ключом ISAM, используя 6-значный номер ссуды в качестве индекса в битовом массиве. Слишком быстро и спустя 8 месяцев доказали, что система мэйнфреймов, с которой мы получали данные, действительно работала неправильно. Простота битовых массивов делает уверенность в их правильности очень высокой - по сравнению с поисковым подходом, например.
bool
. Может быть, даже 4 байта для установок C89, которые используют int
для реализацииbool
Расширяя 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;
}
Попробуйте одну из этих функций на языке 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;
}
value << n
может вызвать неопределенное поведениеВот некоторые макросы, которые я использую:
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)
Если вы хотите выполнить всю эту операцию с программированием на 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 и полезны для обеспечения согласованности между процессорами.
Как вы устанавливаете, очищаете и переключаете один бит?
Для устранения распространенной ошибки кодирования при попытке сформировать маску:
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);
. Есть ли портативный способ сделать это по позиции? Шаблонная версия C ++ 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;}
}
;
после определения вашей функции?)(variable & bits == bits)
? ((variable & bits) == bits)
std::bitset
в c ++ 11int 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);
}
Переменная используется
int value, pos;
value - Data
pos - позиция бита, который нам нужно установить, очистить или переключить.
Установите немного:
value = value | 1 << pos;
Очистить немного:
value = value & ~(1 << pos);
Переключить немного:
value = value ^ 1 << pos;
Давайте предположим, что сначала
num = 55
Integer выполняет побитовые операции (set, get, clear, toggle).
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
. - Выполните побитовое дополнение с приведенным выше результатом. Так что п - й бит становится отключенной и отдыхатъ бит становится установлено ИЭ
~ (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)
Рекомендуемое чтение - Битовые упражнения оператора