Как вы устанавливаете, очищаете и немного переключаетесь?
Как вы устанавливаете, очищаете и немного переключаетесь?
Ответы:
Используйте побитовый оператор ИЛИ ( |
), чтобы установить бит.
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
бита 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.
Использование стандартной библиотеки 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 допускает размер набора битов времени выполнения по сравнению со стандартным набором битов размера библиотеки .
Другой вариант - использовать битовые поля:
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
Это работает только с битовыми полями фиксированного размера. В противном случае вам придется прибегнуть к методам битового поворота, описанным в предыдущих постах.
Я использую макросы, определенные в заголовочном файле, для обработки установленных битов и очистки:
/* 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))
Иногда стоит использовать enum
для именования биты:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
Затем используйте имена позже. То есть пиши
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
установить, очистить и проверить. Таким образом вы скрываете магические числа от остальной части вашего кода.
Кроме этого я одобряю решение Джереми.
clearbits()
функцию вместо &= ~
. Почему вы используете enum для этого? Я думал, что они предназначены для создания набора уникальных переменных со скрытым произвольным значением, но вы назначаете определенное значение каждой из них. Так в чем же выгода от определения их как переменных?
enum
s для наборов связанных констант имеет большое значение в программировании на c. Я подозреваю, что с современными компиляторами единственное преимущество по сравнению с const short
тем, что они явно сгруппированы вместе. И когда вы хотите , чтобы они что - то другое , чем битмаски вы получаете автоматическую нумерацию. В C ++, конечно, они также формируют различные типы, что дает вам немного больше статической проверки ошибок.
enum ThingFlags
значение ThingError|ThingFlag1
, например?
int
. Это может вызывать любые тонкие ошибки из-за неявного целочисленного продвижения или побитовых операций со знаковыми типами. thingstate = ThingFlag1 >> 1
будет, например, вызывать поведение, определяемое реализацией. thingstate = (ThingFlag1 >> x) << y
может вызвать неопределенное поведение. И так далее. Чтобы быть в безопасности, всегда приводите к типу без знака.
enum My16Bits: unsigned short { ... };
/*
** 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
)
Используйте побитовые операторы: &
|
Чтобы установить последний бит в 000b
:
foo = foo | 001b
Чтобы проверить последний бит в foo
:
if ( foo & 001b ) ....
Чтобы очистить последний бит в foo
:
foo = foo & 110b
Я использовал XXXb
для ясности. Вы, вероятно, будете работать с представлением HEX, в зависимости от структуры данных, в которую вы упаковываете биты.
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
Для новичка я хотел бы объяснить немного больше с примером:
Пример:
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)
Вот мой любимый битовый арифметический макрос, который работает для любого типа целочисленного массива без знака от 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
до разделения. Только проблема с очень большими битовыми массивами. Все еще интересный макрос.
Поскольку это помечено как «встроенный», я предполагаю, что вы используете микроконтроллер. Все вышеперечисленные предложения действительны и работают (чтение-изменение-запись, объединения, структуры и т. Д.).
Тем не менее, во время отладки на основе осциллографа я был поражен, обнаружив, что эти методы имеют значительные издержки в циклах ЦП по сравнению с записью значения непосредственно в регистры PORTnSET / PORTnCLEAR микро, что дает реальную разницу, когда есть узкие петли / высокий переключатели ISR
Для тех, кто незнаком: в моем примере микро имеет общий регистр состояния выводов PORTn, который отражает выходные выводы, поэтому выполнение PORTn | = BIT_TO_SET приводит к чтению-модификации-записи в этот регистр. Однако регистры PORTnSET / PORTnCLEAR принимают «1» для обозначения «пожалуйста, сделайте этот бит 1» (SET) или «пожалуйста, сделайте этот бит нулевым» (CLEAR) и «0» для «оставьте пин-код в покое». Таким образом, вы получите два адреса портов в зависимости от того, устанавливаете ли вы или очищаете бит (не всегда удобно), но гораздо более быстрая реакция и меньший собранный код.
volatile
и, следовательно, компилятор не сможет выполнить какую-либо оптимизацию кода, включающего такие регистры. Поэтому хорошей практикой является дизассемблирование такого кода и просмотр его результатов на уровне ассемблера.
Подход битового поля имеет другие преимущества во встроенной области. Вы можете определить структуру, которая отображается непосредственно на биты в конкретном аппаратном регистре.
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
(и фактически вы делаете свой код хуже, делая это)
Эта программа должна изменить любой бит данных с 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);
}
}
}
Если вы делаете много трюков, вы можете использовать маски, которые сделают все это быстрее. Следующие функции очень быстрые и гибкие (они позволяют переворачивать биты в битовых картах любого размера).
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);
Вы должны убедиться, что номер бита находится в пределах диапазона битовой карты, которую вы передаете. Обратите внимание, что для процессоров с прямым порядком байтов байты, слова, слова, слова и т. Д. Правильно отображаются в памяти (основная причина, по которой процессоры с прямым порядком байтов 'лучше', чем процессоры с прямым порядком байтов, ах, я чувствую грядущую войну пламени на...).
Использовать этот:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
Расширяя 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 в ядре 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 и полезны для обеспечения согласованности между процессорами.
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
Вот некоторые макросы, которые я использую:
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)
Переменная используется
int value, pos;
value - Data
pos - позиция бита, который нам нужно установить, очистить или переключить.
Установить немного:
value = value | 1 << pos;
Очистить немного:
value = value & ~(1 << pos);
Переключить немного:
value = value ^ 1 << pos;
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);
}
check_nth_bit
можно bool
.
Давайте предположим, что сначала
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)
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)
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 ^ 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)
Рекомендуемое чтение - Битовые упражнения оператора
Как вы устанавливаете, очищаете и переключаете один бит?
Для устранения распространенной ошибки кодирования при попытке сформировать маску:
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 ++ 11
Эта программа основана на вышеуказанном решении @ 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
Попробуйте одну из этих функций на языке 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
может вызвать неопределенное поведение