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

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

7.09.2008 00:42:17
прочитайте это: graphics.stanford.edu/~seander/bithacks.html и, когда вы
ugasoft 18.09.2008 16:01:18
Вы также можете быть заинтересованы в проверке из битового Twiddler , с битами Хаки и агрегированные Волшебные алгоритмами .
anon 5.01.2009 11:13:32
27 ОТВЕТОВ
РЕШЕНИЕ

Установка немного

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

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;

Это поместит значение nth-го бита 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.

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

3548
19.02.2019 22:48:08
Я хотел бы отметить, что на платформах, которые имеют встроенную поддержку установки / сброса битов (например, микроконтроллеры AVR), компиляторы часто переводят 'myByte | = (1 << x)' в собственные инструкции установки / сброса битов, когда x равен константа, например: (1 << 5) или const без знака x = 5.
Aaron 17.09.2008 17:13:35
бит = число & (1 << x); не будет помещать значение бита x в бит, если только бит не имеет тип _Bool (<stdbool.h>). В противном случае, бит = !! (число & (1 << x)); будет ..
Chris Young 16.11.2008 07:49:52
почему бы вам не сменить последний наbit = (number >> x) & 1
aaronman 26.06.2013 18:47:45
1это intлитерал, который подписан. Таким образом, все операции здесь выполняются на подписанных номерах, что не очень хорошо определено стандартами. Стандарты не гарантируют двоичного дополнения или арифметического сдвига, поэтому его лучше использовать 1U.
Siyuan Ren 10.12.2013 08:53:35
Я предпочитаю number = number & ~(1 << n) | (x << n);менять n-й бит на x.
jiasli 24.03.2015 00:38:31

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

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

foo = foo | 001b

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

if ( foo & 001b ) ....

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

foo = foo & 110b

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

37
12.04.2018 11:23:14
В C. нет двоичных обозначений. Двоичные целочисленные константы являются нестандартным расширением.
Lundin 14.12.2015 09:14:50
Используйте XOR, чтобы немного переключиться:foo = foo ^ MY_MASK
Peter L 29.10.2019 17:54:47
Используйте НЕ, чтобы инвертировать маску, чтобы очистить:foo = foo & ~MY_MASK
Peter L 29.10.2019 17:57:45

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

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

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

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

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

Кроме этого я одобряю решение Джереми.

114
18.10.2013 15:54:21
Поочередно вы можете сделать clearbits()функцию вместо &= ~. Почему вы используете перечисление для этого? Я думал, что они предназначены для создания набора уникальных переменных со скрытым произвольным значением, но вы назначаете определенное значение каждой из них. Так в чем же выгода от определения их как переменных?
endolith 20.12.2011 15:09:13
@endolith: использование enums для наборов связанных констант имеет большое значение в программировании на c. Я подозреваю, что с современными компиляторами единственное преимущество по сравнению с const shortтем, что они явно сгруппированы вместе. И когда вы хотите , чтобы они что - то другое , чем битмаски вы получаете автоматическую нумерацию. В C ++, конечно, они также формируют различные типы, что дает вам немного больше статической проверки ошибок.
dmckee --- ex-moderator kitten 22.12.2011 01:15:44
Вы попадете в неопределенные константы перечисления, если не определите константу для каждого из возможных значений битов. Какое enum ThingFlagsзначение для ThingError|ThingFlag1, например?
Luis Colorado 30.09.2014 10:55:14
Если вы используете этот метод, имейте в виду, что константы перечисления всегда имеют тип со знаком int. Это может вызывать любые тонкие ошибки из-за неявного целочисленного продвижения или побитовых операций со знаковыми типами. thingstate = ThingFlag1 >> 1будет, например, вызывать поведение, определяемое реализацией. thingstate = (ThingFlag1 >> x) << yможет вызвать неопределенное поведение. И так далее. Чтобы быть в безопасности, всегда приводите к типу без знака.
Lundin 14.12.2015 09:25:32
@Lundin: Начиная с C ++ 11, вы можете установить базовый тип перечисления, например: enum My16Bits: unsigned short { ... };
Aiken Drum 15.03.2016 15:01:59

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

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

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

245
29.11.2012 00:40:32
Я всегда считал использование битовых полей плохой идеей. У вас нет контроля над порядком, в котором распределяются биты (сверху или снизу), что делает невозможным сериализацию значения стабильным / переносным способом, кроме как по битам. Также невозможно смешать битовую арифметику DIY с битовыми полями, например, создать маску, которая проверяет несколько битов одновременно. Конечно, вы можете использовать && и надеяться, что компилятор оптимизирует его правильно ...
R.. GitHub STOP HELPING ICE 28.06.2010 06:17:34
Битовые поля плохи во многих отношениях, я мог бы почти написать книгу об этом. Фактически, мне почти пришлось сделать это для небольшой программы, которая требовала соответствия требованиям MISRA-C. MISRA-C принудительно документирует все поведение, определяемое реализацией, поэтому я написал довольно эссе обо всем, что может пойти не так в битовых полях. Порядок битов, порядковый номер, биты заполнения, байты заполнения, различные другие проблемы выравнивания, неявные и явные преобразования типов в и из битового поля, UB, если int не используется, и так далее. Вместо этого используйте побитовые операторы для уменьшения количества ошибок и переносимого кода. Битовые поля полностью избыточны.
Lundin 18.08.2011 19:19:51
Как и большинство языковых функций, битовые поля можно использовать правильно или ими можно злоупотреблять. Если вам нужно упаковать несколько небольших значений в одно целое, битовые поля могут быть очень полезны. С другой стороны, если вы начинаете делать предположения о том, как битовые поля отображаются на фактическое, содержащее int, вы просто напрашиваетесь на неприятности.
Ferruccio 18.08.2011 19:35:12
@endolith: Это не будет хорошей идеей. Вы можете заставить его работать, но он не обязательно будет переносимым на другой процессор, или на другой компилятор, или даже на следующую версию того же компилятора.
Ferruccio 8.03.2012 21:02:38
@Yasky и Ferruccio, получающие разные ответы на sizeof () для этого подхода, должны проиллюстрировать проблемы совместимости не только между компиляторами, но и с оборудованием. Иногда мы обманываем себя тем, что решили эти проблемы с помощью языков или определенных сред выполнения, но на самом деле все сводится к тому, «будет ли это работать на моей машине?». Вы, ребята, вложенные в меня, уважаете (и симпатии).
Kelly S. French 8.12.2016 16:11:42

Из 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] ------------------------------------------- ----------------------

47
8.06.2019 06:39:22
2 вещи об этом: (1) при просмотре ваших макросов, некоторые могут ошибочно полагать, что макросы фактически устанавливают / сбрасывают / переворачивают биты в аргументе, однако назначения нет; (2) ваш test.c не завершен; Я подозреваю, что если бы вы запустили больше дел, вы бы обнаружили проблему (упражнение для чтения)
Dan 18.10.2008 01:51:34
-1 Это просто странное запутывание. Никогда не изобретайте язык C, скрывая синтаксис языка за макросами, это очень плохая практика. Затем некоторые странности: сначала подписывается 1L, что означает, что все битовые операции будут выполняться со знаком типа. Все, что передано в эти макросы, вернется как подписано долго. Не хорошо. Во-вторых, это будет очень неэффективно работать на меньших процессорах, так как оно будет работать долго, когда операции могли выполняться на уровне int. В-третьих, функционально-подобные макросы являются корнем всего зла: у вас нет никакой безопасности типов. Кроме того, предыдущий комментарий об отсутствии назначения очень действителен.
Lundin 18.08.2011 19:14:17
Это не удастся, если argесть long long. 1Lдолжен быть максимально широкого типа, так (uintmax_t)1. (Вы можете сойти с рук 1ull)
M.M 6.02.2015 23:51:47
Вы оптимизировали для размера кода? На основных процессорах Intel вы получите частичный регистр при чтении AX или EAX после того, как эта функция вернется, потому что она записывает 8-битные компоненты EAX. (Это нормально для процессоров AMD или других, которые не переименовывают частичные регистры отдельно от полного регистра. Haswell / Skylake не переименовывают AL отдельно, но они переименовывают AH. ).
Peter Cordes 10.11.2017 21:38:16

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

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

Это зависит от вас, чтобы убедиться, что номер бита находится в пределах диапазона битовой карты, которую вы передаете. Обратите внимание, что для процессоров с прямым порядком байтов байты, слова, слова, слова и т. Д. Правильно отображаются в памяти друг друга (основная причина, по которой процессоры с прямым порядком байтов 'лучше', чем процессоры с прямым порядком байтов, ах, я чувствую грядущую войну пламени на...).

14
5.12.2017 11:20:35
Не используйте таблицу для функции, которая может быть реализована одним оператором. TQuickByteMask [n] эквивалентно (1 << n). Кроме того, короткая аргументация - очень плохая идея. / И% на самом деле будет делением, а не битовым сдвигом / побитовым, и потому что знаковое деление со степенью 2 не может быть реализовано побитовым. Вы должны сделать тип аргумента unsigned int!
R.. GitHub STOP HELPING ICE 28.06.2010 06:24:57
Какой в ​​этом смысл? Это только делает код медленнее и сложнее для чтения? Я не вижу в этом ни одного преимущества. 1u << n легче читать для программистов на C, и, мы надеемся, может быть преобразовано в инструкцию CPU с одним тактом. С другой стороны, ваше деление будет переведено примерно в 10 тиков, или даже так плохо, как до 100 тиков, в зависимости от того, насколько плохо конкретная архитектура справляется с делением. Что касается функции растрового изображения, то было бы более разумно иметь справочную таблицу, переводящую каждый битовый индекс в байтовый индекс, чтобы оптимизировать скорость.
Lundin 18.08.2011 19:32:14
Что касается big / little endian, big endian будет отображать целые числа и необработанные данные (например, строки) одинаково: слева направо от msb до lsb по всему растровому изображению. Хотя little endian будет отображать целые числа слева направо как 7-0, 15-8, 23-18, 31-24, но необработанные данные все еще остаются слева направо от msb до lsb. Так что, как мало endian лучше для вашего конкретного алгоритма, полностью вне меня, кажется, наоборот.
Lundin 18.08.2011 19:42:44
@R .. Таблица может быть полезна, если ваша платформа не может эффективно сдвигаться, как старые микрочипы MCU, но, конечно, тогда деление в выборке абсолютно неэффективно
jeb 18.11.2011 11:28:14

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

456
16.02.2018 01:18:57
+1. Не то, чтобы std :: bitset можно было использовать из "C", но поскольку автор отметил свой вопрос как "C ++", AFAIK, ваш ответ здесь самый лучший ... std :: vector <bool> - это другой способ, если кто-то знает его плюсы и минусы
paercebal 19.09.2008 18:16:50
@andrewdotnich: vector <bool> - это (к сожалению) специализация, которая хранит значения в виде битов. См. Gotw.ca/publications/mill09.htm для получения дополнительной информации ...
Niklas 12.12.2008 20:40:15
Возможно, никто не упомянул это, потому что это было помечено как встроенное В большинстве встроенных систем вы избегаете STL, как чума. И поддержка буста, вероятно, является очень редкой птицей среди большинства встроенных компиляторов.
Lundin 18.08.2011 19:47:06
@ Мартин Это очень верно. Помимо конкретных факторов, снижающих производительность, таких как STL и шаблоны, многие встроенные системы даже полностью избегают использования стандартных библиотек, потому что их очень сложно проверить. Большая часть встраиваемой ветви охватывает стандарты, такие как MISRA, для которых требуются инструменты статического анализа кода (кстати, такие инструменты должны использовать любые профессионалы в области программного обеспечения, а не просто встраиваемые люди). Обычно у людей есть дела поважнее, чем проводить статический анализ через всю стандартную библиотеку - если ее исходный код даже доступен для них на конкретном компиляторе.
Lundin 19.08.2011 06:26:06
@Lundin: Ваши высказывания слишком широки (поэтому спорить бесполезно). Я уверен, что смогу найти ситуации, если они правдивы. Это не меняет мою начальную точку. Оба эти класса прекрасно подходят для использования во встроенных системах (и я точно знаю, что они используются). Ваша первоначальная точка зрения о том, что STL / Boost не используется во встроенных системах, также неверна. Я уверен, что есть системы, которые их не используют, и даже те системы, которые их используют, они используются разумно, но говорить, что они не используются, просто не правильно (потому что есть системы, в которых они используются).
Martin York 19.08.2011 06:41:32

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

/* 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))
181
23.10.2018 14:22:17
Э-э, я понимаю, что это 5-летняя запись, но ни в одном из этих макросов нет дублирования аргументов, Дэн
Robert Kelly 2.10.2013 14:53:25
BITMASK_CHECK(x,y) ((x) & (y))в ((x) & (y)) == (y)противном случае он возвращает неверный результат для многобитовой маски (например, 5против 3) / * Привет всем могильщикам :) * /
brigadir 11.12.2014 12:00:52
1должно быть (uintmax_t)1или похоже на случай, если кто-то попытается использовать эти макросы для более longкрупного типа
M.M 6.02.2015 23:50:06
BITMASK_CHECK_ALL(x,y)может быть реализовано как!~((~(y))|(x))
Handy999 20.11.2018 09:24:42
@ Handy999 Немного легче понять, почему это работает после применения закона Де Моргана и реорганизации, чтобы получить!(~(x) & (y))
Tavian Barnes 13.08.2019 20:14:54

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

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

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

24
6.11.2008 11:30:16
Практически все, что касается битовых полей, определяется реализацией. Даже если вам удастся выяснить все детали относительно того, как ваш конкретный компилятор реализует их, использование их в вашем коде наверняка сделает его непереносимым.
Lundin 18.08.2011 19:50:26
@Lundin - Правда, но битовая встроенная система (особенно в аппаратных регистрах, к которой относится мой ответ) никогда не будет полезной в любом случае.
Roddy 19.08.2011 20:13:25
Возможно, не между совершенно разными процессорами. Но вы, скорее всего, хотите, чтобы он был переносимым между компиляторами и между различными проектами. И есть много встроенных «разборов», которые вообще не связаны с аппаратным обеспечением, таких как кодирование / декодирование протокола данных.
Lundin 20.08.2011 09:35:20
... и если вы привыкнете использовать битовые поля во встроенном программировании, вы обнаружите, что ваш код X86 работает быстрее и эффективнее. Не в простых тестах, где у вас есть целая машина, чтобы преодолеть тест, а в реальных многозадачных средах, где программы конкурируют за ресурсы. Преимущество CISC - первоначальная цель которого заключалась в том, чтобы компенсировать процессоры быстрее, чем шины и медленную память.
user1899861 15.02.2013 22:26:53

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

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

20
3.01.2009 23:44:14

Использовать это:

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

    return num;
}
12
22.05.2015 16:06:03
Ну, это использует неэффективное ветвление.
asdf 2.07.2011 19:33:00
@asdf Задача компилятора - вывести наиболее эффективный двоичный файл, а задача программиста - написать понятный код
M.M 6.02.2015 23:53:56
Это хорошая демонстрация тестирования, установки и очистки определенного бита. Тем не менее, это очень плохой подход для переключения.
Ben Voigt 22.02.2015 02:18:34

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

#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)))
20
15.06.2009 07:38:21
CHAR_BITуже определено limits.h, вам не нужно вставлять свои собственные BITS(и фактически вы делаете свой код хуже, делая это)
M.M 6.02.2015 23:52:34

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

и т.п.

26
9.05.2013 14:21:00
Это хорошо читать, но нужно знать о возможных побочных эффектах. Использование BITOP(array, bit++, |=);в цикле, скорее всего, не будет делать то, что хочет вызывающий.
foraidt 13.07.2010 08:27:40
В самом деле. =) Один вариант, который вы можете предпочесть, состоит в том, чтобы разделить его на 2 макроса, 1 для адресации элемента массива и другой для смещения бита на место, аля BITCELL(a,b) |= BITMASK(a,b);(оба принимают aв качестве аргумента для определения размера, но последний никогда не будет вычислять, aтак как это появляется только в sizeof).
R.. GitHub STOP HELPING ICE 13.07.2010 09:19:46
@R .. Этот ответ действительно старый, но в этом случае я бы предпочел функцию макросу.
PC Luddite 23.10.2015 17:08:19
Minor: 3 - й (size_t)бросок , кажется, есть только обеспечить некоторую беззнаковую математику с %. Мог (unsigned)там.
chux - Reinstate Monica 27.09.2017 17:58:06
(size_t)(b)/(8*sizeof *(a))Ненужно может быть сужена bдо разделения. Только проблема с очень большими битовыми массивами. Все еще интересный макрос.
chux - Reinstate Monica 27.09.2017 18:00:43

Эта программа должна изменить любой бит данных с 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);
        }
    }
}
14
22.05.2015 16:07:42

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

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

6
5.07.2012 22:58:36
0xэто префикс для литерала в шестнадцатеричном, а не двоичном.
Ben Voigt 22.02.2015 02:20:34

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

Пример:

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)
32
22.05.2015 16:09:54

Поскольку это помечено как «встроенный», я предполагаю, что вы используете микроконтроллер. Все вышеперечисленные предложения действительны и работают (чтение-изменение-запись, объединения, структуры и т. Д.).

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

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

25
14.06.2012 15:23:17
Микро был Coldfire MCF52259, используя C в Codewarrior. Рассмотрение дизассемблера / ассемблера является полезным упражнением, поскольку оно показывает все шаги, которые ЦП должен пройти, чтобы выполнить даже самые основные операции. <br> Мы также заметили другие инструкции по переключению ЦП в циклы, критичные ко времени - ограничение переменной путем выполнения var% = max_val стоит кучу циклов ЦП каждый раз, в то время как мы выполняем, если (var> max_val) var- = max_val использует только пара инструкций. <br> Хорошее руководство по нескольким трюкам здесь: codeproject.com/Articles/6154/…
John U 19.06.2012 17:33:18
Еще важнее то, что регистры ввода-вывода с отображением вспомогательной памяти обеспечивают механизм атомарных обновлений. Чтение / изменение / запись могут идти очень плохо, если последовательность прерывается.
Ben Voigt 22.02.2015 02:16:20
Имейте в виду, что все регистры портов будут определены как, volatileи, следовательно, компилятор не сможет выполнить какую-либо оптимизацию кода, включающего такие регистры. Поэтому хорошей практикой является дизассемблирование такого кода и просмотр его результатов на уровне ассемблера.
Lundin 14.12.2015 09:42:09

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

9
20.03.2020 13:58:02
std :: bitset действительно реализован в виде битов большинством компиляторов
galinette 17.11.2014 20:03:37
@galinette, Согласен. Заголовочный файл #include <bitset> является хорошим ресурсом в этом отношении. Кроме того, специальный класс vector <bool> для случаев, когда вам нужно изменить размер вектора. C ++ STL, 2nd Edition, Nicolai M. Josuttis подробно описывает их на страницах 650 и 281 соответственно. C ++ 11 добавляет несколько новых возможностей в std :: bitset, особый интерес для меня представляет хеш-функция в неупорядоченных контейнерах. Спасибо за головы! Я собираюсь удалить свой комментарий о судорогах. Уже достаточно мусора в сети. Я не хочу добавлять к этому.
user1899861 17.11.2014 21:08:22
Это использует по крайней мере целый байт памяти для каждого bool. Может быть, даже 4 байта для установок C89, которые используют intдля реализацииbool
M.M 6.02.2015 23:55:27
@ MattMcNabb, вы правы. В C ++ размер типа int, необходимого для реализации логического значения, не указан стандартом. Я понял, что этот ответ был ошибочным некоторое время назад, но решил оставить его здесь, так как люди, по-видимому, находят его полезным. Для тех, кто хочет использовать биты, наиболее полезен комментарий Галинетт, как и моя библиотека битов здесь ... stackoverflow.com/a/16534995/1899861
user1899861 12.02.2015 07:23:47
@RocketRoy: Возможно, стоит изменить предложение, которое утверждает, что это пример «битовых операций».
Ben Voigt 22.02.2015 02:20:04

Расширяя 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;
}
10
8.05.2014 04:33:41

Попробуйте одну из этих функций на языке 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;
}
-2
16.02.2018 01:20:23
value << nможет вызвать неопределенное поведение
M.M 6.02.2015 23:57:15

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

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)
5
6.02.2015 23:11:18

Если вы хотите выполнить всю эту операцию с программированием на C в ядре Linux, тогда я предлагаю использовать стандартные API ядра Linux.

См. Https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html.

set_bit  Atomically set a bit in memory
clear_bit  Clears a bit in memory
change_bit  Toggle a bit in memory
test_and_set_bit  Set a bit and return its old value
test_and_clear_bit  Clear a bit and return its old value
test_and_change_bit  Change a bit and return its old value
test_bit  Determine whether a bit is set

Примечание: здесь вся операция происходит за один шаг. Таким образом, все они гарантированно являются атомарными даже на компьютерах SMP и полезны для обеспечения согласованности между процессорами.

10
1.12.2017 16:21:28

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

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

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

4
27.09.2017 18:18:48
Интересный взгляд на старый вопрос! Ни уместно, number |= (type_of_number)1 << x;ни number |= (number*0 + 1) << x;устанавливать бит знака знакового типа ... На самом деле, это не так number |= (1ull << x);. Есть ли портативный способ сделать это по позиции?
chqrlie for yellow blockquotes 27.09.2017 22:27:57
@chqrlie IMO, лучший способ избежать установки знакового бита и риска UB или IDB со сдвигами - это использовать неподписанные типы. Высокопереносимый код со сдвигом и подписью слишком сложен, чтобы быть приемлемым.
chux - Reinstate Monica 27.09.2017 22:33:49

Шаблонная версия 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;}
}
3
27.02.2018 20:51:05
Этот код не работает. (Кроме того, почему у вас есть ;после определения вашей функции?)
melpomene 10.02.2018 20:11:47
@ melpomene Код не сломан, я его проверил. Ты имеешь в виду, что он не скомпилируется или результат неверный? О лишних ';' Я не помню, они действительно могут быть удалены.
Joakim L. Christiansen 25.02.2018 15:51:11
(variable & bits == bits)?
melpomene 25.02.2018 18:25:33
Спасибо за то, что заметили, это должно было быть((variable & bits) == bits)
Joakim L. Christiansen 27.02.2018 20:56:01
использовать std::bitsetв c ++ 11
pqnet 25.10.2019 20:27:22
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);
}
5
8.06.2019 06:39:59

Переменная используется

int value, pos;

value - Data
pos - позиция бита, который нам нужно установить, очистить или переключить.

Установите немного:

value = value | 1 << pos;

Очистить немного:

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

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

value = value ^ 1 << pos;
5
8.06.2019 06:38:38

Давайте предположим, что сначала
num = 55 Integer выполняет побитовые операции (set, get, clear, toggle).
n = 40 битовая позиция для выполнения побитовых операций.

Как получить немного?

  1. Чтобы получить 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. Чтобы установить конкретный бит числа. Сдвиг влево 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. Сдвиг влево 1, nто есть раз 1 << n.
  2. Выполните побитовое дополнение с приведенным выше результатом. Так что п - й бит становится отключенной и отдыхатъ бит становится установлено ИЭ ~ (1 << n).
  3. Наконец, выполните побитовую &операцию И с вышеуказанным результатом и 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)

Рекомендуемое чтение - Битовые упражнения оператора

5
10.06.2019 06:12:46
Спасибо за подробное объяснение. Вот ссылка для практической задачи для BIT Magic link
Chandra Shekhar 28.12.2019 18:37:41