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

вопрос создан в Tue, Jun 12, 2018 12:00 AM

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

    
2354
  1. прочитайте это: graphics.stanford .edu /~ seander /bithacks.html и, когда вы освоите это, прочитайте это: realtimecollisiondetection.net/blog/?p=78
    2008-09-18 16: 01: 18Z
  2. Вам также может быть интересно проверить Битовый Twiddler , Бит Хакерские игры и агрегированные алгоритмы магии .
    2009-01-05 11: 13: 32Z
  3. Я могу легко представить, что это было не для самого вопроса, а для создания очень полезного справочного руководства. Учитывая, что я попал сюда, когда мне нужно было немного информации, в любом случае.
    2013-08-11 21: 21: 16Z
  4. @ glglgl /Jonathon имеет достаточно значения для C ++, чтобы пометить его так. Это исторический вопрос с большим количеством трафика, и тег C ++ поможет другим заинтересованным программистам найти его через поиск в Google.
    2013-11-04 18: 57: 54Z
  5. Я предполагаю, что вы знаете, как включить /выключить отдельные биты в байте? (Например, используя hex?)
    2016-04-19 19: 30: 32Z
27 ответов                              27                         

Установка бита

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

number |= 1UL << n;

Это установит n-й бит number. n должно быть равно нулю, если вы хотите установить 1-й бит и т. д. до n-162626262626262, если вы хотите установить бит 0600001111000011, если вы хотите установить 0600.

Используйте n, если 1ULL шире, чем number; продвижение unsigned long не произойдет до тех пор, пока после оценки 1UL << n не будет определено поведение смещения более чем на ширину 1UL << n. То же самое относится ко всем остальным примерам.

Немного очищаем

Используйте побитовый оператор AND (long), чтобы немного очистить.

&

Это очистит

number &= ~(1UL << n);
-й бит n. Вы должны инвертировать битовую строку с помощью побитового оператора NOT (number), затем AND it.

Немного переключиться

Оператор XOR (~) можно использовать для переключения немного.

^

Это переключит

number ^= 1UL << n;
-й бит n.

Немного проверяю

Вы не просили об этом, но я мог бы также добавить это.

Чтобы проверить немного, сдвиньте число n вправо, затем поразрядно И это:

number

Это поместит значение

bit = (number >> n) & 1U;
-го бита n в переменную number.

Изменение бита n на x

Установка bit-го бита в n или в 1 может быть достигнута с помощью следующего в реализации C ++ дополнения 2:

0

Бит

number ^= (-x ^ number) & (1UL << n);
будет установлен, если n равен x, и сброшен, если 1 равен x. Если 0 имеет какое-либо другое значение, вы получаете garbage. x будет логизировать егодо 0 или 1.

Чтобы сделать это независимым от поведения отрицания дополнения 2 (где x = !!x имеет все установленные биты, в отличие от реализации 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 установит бит (x << n)62 на n.

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

    
3333
2019-02-19 22: 48: 08Z
  1. Я хотел бы отметить, что на платформах, которые имеют встроенную поддержку установки /сброса битов (например, микроконтроллеры AVR), компиляторы часто переводят 'myByte | = (1 &lt ; < x) 'в собственные инструкции установки /сброса битов, когда x является константой, например: (1 < < 5) или const unsigned x = 5.
    2008-09-17 17: 13: 35Z
  2. бит = число & (1 < < x); не будет помещать значение бита x в бит, если только бит не имеет тип _Bool (< stdbool.h >). В противном случае, бит = !! (число & (1 < < x)); будет ..
    2008-11-16 07: 49: 52Z
  3. почему бы вам не изменить последний на x
    2013-06-26 18: 47: 45Z
  4. bit = (number >> x) & 1 - это литерал 1, который подписан. Таким образом, все операции здесь выполняются на подписанных номерах, что не очень хорошо определено стандартами. Стандарты не гарантируют двоичное дополнение или арифметическое смещение, поэтому лучше использовать int.
    2013-12-10 08: 53: 35Z
  5. Я предпочитаю 1U для изменения n-го бита на x.
    2015-03-24 00: 38: 31Z

Использование стандартной библиотеки C ++: number = number & ~(1 << n) | (x << n); .

Или Boost версия: std::bitset<N> .

Нет необходимости кататься самостоятельно:

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

Boost-версия допускает размер набора битов во время выполнения по сравнению с компиляцией стандартной библиотеки размерный набор битов.

    
432
2018-02-16 01: 18: 57Z
  1. + 1. Не то, чтобы std :: bitset можно было использовать из "C", но, поскольку автор отметил свой вопрос как "C ++", AFAIK, ваш ответ здесь самый лучший ... std :: vector < bool > это другой способ, если кто-то знает его плюсы и минусы
    2008-09-19 18: 16: 50Z
  2. @ andrewdotnich: vector < bool > это (к сожалению) специализация, которая хранит значения в виде битов. См. gotw.ca/publications/mill09.htm для получения дополнительной информации ...
    2008-12-12 20: 40: 15Z
  3. Возможно, никто не упомянул об этом, потому что это было помечено как встроенное. В большинстве встроенных систем вы избегаете STL, как чумы. И поддержка буста, вероятно, является очень редкой птицей среди самыхompilers.
    2011-08-18 19: 47: 06Z
  4. @ Martin Это очень верно. Помимо определенных факторов, снижающих производительность, таких как STL и шаблоны, многие встроенные системы даже полностью избегают использования стандартных библиотек, потому что их очень сложно проверить. Большая часть встраиваемой ветви охватывает стандарты, такие как MISRA, для которых требуются инструменты статического анализа кода (кстати, такие инструменты должны использовать любые профессионалы в области программного обеспечения, а не просто встраиваемые люди). Обычно у людей есть дела поважнее, чем проводить статический анализ через всю стандартную библиотеку - если ее исходный код даже доступен для них на конкретном компиляторе.
    2011-08-19 06: 26: 06Z
  5. @ Lundin: Ваши высказывания слишком широки (поэтому спорить бесполезно). Я уверен, что смогу найти ситуации, если они правдивы. Это не меняет мою начальную точку. Оба эти класса прекрасно подходят для использования во встроенных системах (и я точно знаю, что они используются). Ваше первоначальное мнение о том, что STL /Boost не используется во встроенных системах, также неверно. Я уверен, что есть системы, которые их не используют, и даже те системы, которые их используют, они используются разумно, но сказать, что они не используются, просто неправильно (потому что есть системы, в которых они используются).
    2011-08-19 06: 41: 32Z

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

[Alpha:] > ./a.out
00010

определяет 3-битное поле (на самом деле это три 1-битных поля). Битовые операции теперь стали немного (ха-ха) проще:

Чтобы установить или очистить немного:

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

Чтобы немного переключиться:

mybits.b = 1;
mybits.c = 0;

Немного проверяю:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

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

    
228
2012-11-29 00: 40: 32Z
  1. Я всегда считал использование битовых полей плохой идеей. У вас нет контроля над порядком, в котором распределяются биты (сверху или снизу), что делает невозможным сериализацию значения стабильным /переносным способом, кроме как по битам. Также невозможно смешать битовую арифметику DIY с битовыми полями, например, создать маску, которая проверяет несколько битов одновременно. Конечно, вы можете использовать & и надеюсь, что компилятор оптимизирует его правильно ...
    2010-06-28 06: 17: 34Z
  2. Битовые поля плохи во многих отношениях, я мог бы почти написать книгу об этом. Фактически, мне почти пришлось сделать это для небольшой программы, которая требовала соответствия требованиям MISRA-C. MISRA-C принудительно документирует все поведение, определяемое реализацией, поэтому я написал довольно эссе обо всем, что может пойти не так в битовых полях. Порядок битов, порядковый номер, биты заполнения, байты заполнения, различные другие проблемы выравнивания, неявные и явные преобразования типов в и из битового поля, UB, если int не используется, и так далее. Вместо этого используйте побитовые операторы для уменьшения количества ошибок и переносимого кода. Битовые поля полностью избыточны.
    2011-08-18 19: 19: 51Z
  3. Как и большинство языковых функций, битовые поля можно использовать правильно или ими можно злоупотреблять. Если вам нужно упаковать несколько небольших значений в одно целое, битовые поля могут быть очень полезны. С другой стороны, если вы начинаете делать предположения о том, как битовые поля отображаются на фактическое, содержащее int, вы просто напрашиваетесь на неприятности.
    2011-08-18 19: 35: 12Z
  4. @ endolith: Это не очень хорошая идея. Вы можете заставить его работать, но он не обязательно будет переносимым на другой процессор, или на другой компилятор, или даже на следующую версию того же компилятора.
    2012-03-08 21: 02: 38Z
  5. @ Яски и Ферруччо, получающие разные ответы на sizeof () для этого подхода, должны проиллюстрировать проблемы совместимости не только в разныхmpilers но по железу. Иногда мы обманываем себя, что решили эти проблемы с помощью языков или определенных сред выполнения, но на самом деле все сводится к тому, «будет ли это работать на моей машине? Вы, ребята, встраиваемые, уважайте меня (и симпатии).
    2016-12-08 16: 11: 42Z

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

if (mybits.c)  //if mybits.c is non zero the next line below will execute
    
156
2018-10-23 14: 22: 17Z
  1. Э-э, я понимаю, что это 5-летняя запись, но ни в одном из этих макросов нет дублирования аргументов, Дэн
    2013-10-02 14: 53: 25Z
  2. /* 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) /* Привет всем gravediggers:) *) *
    2014-12-11 12: 00: 52Z
  3. 3 должно быть 1 или аналогичным, если кто-либо пытается использовать эти макросы для типа (uintmax_t)1 или большего размера
    2015-02-06 23: 50: 06Z
  4. Или long работает так же, как 1ULL, в большинстве реализаций.
    2017-11-10 21: 27: 00Z
  5. @ brigadir: зависит от того, хотите ли вы проверить, установлены ли какие-либо биты или все биты установлены. Я обновил ответ, чтобы включить оба с описательными именами.
    2017-11-10 21: 31: 02Z

Иногда стоит использовать (uintmax_t) для присвоения битов:

enum

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

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

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

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

    
111
2013-10-18 15: 54: 21Z
  1. В качестве альтернативы вы можете сделать функцию
    thingstate |= ThingFlag1;
    thingstate &= ~ThingFlag0;
    if (thing & ThingError) {...}
    
    вместо clearbits(). Почему вы используете для этого перечисление? Я думал, что они предназначены для создания набора уникальных переменных со скрытым произвольным значением, но вы назначаете определенное значение каждой. Так в чем же выгода от определения их как переменных?
    2011-12-20 15: 09: 13Z
  2. @ endolith: использование &= ~s для наборов связанных констант имеет большое значение в программировании на языке Си. Я подозреваю, что у современных компиляторов единственным преимуществом перед enum или чем-то еще является то, что они явно сгруппированы вместе. И когда вы хотите их для чего-то другого , кроме битовых масок, вы получаете автоматическую нумерацию. В C ++, конечно, они также формируют различные типы, что дает вам немного больше статической проверки ошибок.
    2011-12-22 01: 15: 44Z
  3. Вы попадете в неопределенные константы перечисления, если не определите константу для каждого из возможных значений битов. Что такое значение const short для enum ThingFlags, например?
    2014-09-30 10: 55: 14Z
  4. Если вы используете этот метод, имейте в виду, что константы перечисления всегда имеют тип со знаком ThingError|ThingFlag1. Это может вызвать всевозможные незначительные ошибки из-за неявного целочисленного продвижения или побитовых операций на подписанных типах. int будет, например, вызывать поведение, определяемое реализацией. thingstate = ThingFlag1 >> 1 может вызывать неопределенное поведение. И так далее. Чтобы быть в безопасности, всегда приводите к типу без знака.
    2015-12-14 09: 25: 32Z
  5. @ Lundin: Начиная с C ++ 11, вы можете установить базовый тип перечисления, например: thingstate = (ThingFlag1 >> x) << y
    2016-03-15 15: 01: 59Z

Из snip-c.zip bitops.h: enum My16Bits: unsigned short { ... };

Хорошо, давайте разберемся ...

Общее выражение, с которым у вас, похоже, возникают проблемы во всех этих случаях, это "(1L < < < < < (posn))". Все это создает маску с одним битом на и который будет работать с любым целочисленным типом. Аргумент "posn" указывает положение, где вы хотите немного. Если posn == 0, то это выражение будет оценить:

/*
**  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)))

Если posn == 8, он оценивается в:

0000 0000 0000 0000 0000 0000 0000 0001 binary.

Другими словами, он просто создает поле из 0 с 1 в указанном позиция. Единственная сложная часть в макросе BitClr (), где нам нужно установить один бит 0 в поле 1. Это достигается с помощью 1 дополнение того же выражения, которое обозначено оператором тильды (~).

После того, как маска создана, она применяется к аргументу, как вы предлагаете, с помощью побитовых и (&), или (|), и xor (^) операторов. С маской имеет тип long, макросы будут работать так же хорошо на char, short, int, или длинные.

Суть в том, что это общее решение для всего класса проблемы. Конечно, возможно и даже уместно переписать эквивалент любого из этих макросов с явными значениями маски каждый раз, когда вы нужен, но зачем это делать? Помните, что подстановка макросов происходит в препроцессор и поэтому сгенерированный код будет отражать тот факт, что значения компилятором считаются постоянными, т. е. его использование также эффективно обобщенные макросы, чтобы «изобретать велосипед» каждый раз, когда вам нужно сделать немного манипуляций.

убежденный? Вот некоторый тестовый код - я использовал Watcom C с полной оптимизацией и без использования _cdecl, чтобы результирующая разборка была бы такой же чистой, как возможно:

---- [TEST.C] -------------------------------------- -------------------------- р>

0000 0000 0000 0000 0000 0001 0000 0000 binary.

---- [TEST.OUT (разобрано)] ----------------------------------- ------------ р>

#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;
}

---- [finis] ---------------------------------------- -------------------------

    
42
2019-06-08 06: 39: 22Z
  1. Об этом есть две вещи: (1) при просмотре ваших макросов некоторые могут ошибочно полагать, что макросы фактически устанавливают /сбрасывают /переворачивают биты в аргументе, однако назначение; (2) ваш test.c не завершен; Я подозреваю, что если вы запустите больше дел, вы найдете проблему (упражнение для чтения)
    2008-10-18 01: 51: 34Z
  2. - 1 Это просто странная путаница. Никогда не изобретайте язык C, скрывая синтаксис языка за макросами, это очень плохая практика. Затем некоторые странности: сначала подписывается 1L, что означает, что все битовые операции будут выполняться со знаком типа. Все, что передано в эти макросы, вернется как подписано долго. Нехорошо. Во-вторых, это будет очень неэффективно работать на меньших процессорах, так как оно будет работать долго, когда операции могли выполняться на уровне int. В-третьих, функционально-подобные макросы являются корнем всего зла: у вас нет никакой безопасности типов. Кроме того, предыдущий комментарий об отсутствии назначения очень действителен.
    2011-08-18 19: 14: 17Z
  3. Это произойдет сбой, если
    Module: C:\BINK\tst.c
    Group: 'DGROUP' CONST,CONST2,_DATA,_BSS
    
    Segment: _TEXT  BYTE   00000008 bytes  
     0000  0c 84             bitmanip_       or      al,84H    ; set bits 2 and 7
     0002  80 f4 02                          xor     ah,02H    ; flip bit 9 of EAX (bit 1 of AH)
     0005  24 f7                             and     al,0f7H
     0007  c3                                ret     
    
    No disassembly errors
    
    равен arg. long long должен быть максимально широкого типа, поэтому 1L. (Вы можете получить с (uintmax_t)1)
    2015-02-06 23: 51: 47Z
  4. Оптимизировали ли вы размер кода? На основных процессорах Intel вы получите частичную регистрацию остановок при чтении AX или EAX afЭта функция возвращает, потому что она записывает 8-битные компоненты EAX. (Это нормально для процессоров AMD или других, которые не переименовывают частичные регистры отдельно от полного регистра. Haswell /Skylake не переименовывает AL отдельно, но переименовывает AH. ).
    2017-11-10 21: 38: 16Z

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

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

000b

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

foo = foo | 001b
: foo

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

if ( foo & 001b ) ....
: foo

Я использовал

foo = foo & 110b
для ясности. Вероятно, вы будете работать с представлением HEX, в зависимости от структуры данных, в которой вы упаковываете биты.     
35
2018-04-12 11: 23: 14Z
  1. Нет двоичной записи в C. Двоичные целочисленные константы являются нестандартным расширением.
    2015-12-14 09: 14: 50Z

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

Пример: р> XXXb

Используется оператор

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)
оператор: установить бит |     
31
2015-05-22 16: 09: 54Z

Вот мой любимый битовый арифметический макрос, который работает для любого типа целочисленного массива без знака от

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, ^=);

и др.

    
25
2013-05-09 14: 21: 00Z
  1. Это хорошо для чтения, но следует помнить о возможных побочных эффектах. Использование
    if (BITOP(array, bit, &)) ...
    
    в цикле, скорее всего, не будет делать то, что хочет вызывающий.
    2010-07-13 08: 27: 40Z
  2. Действительно. =) Один вариант, который вы можете предпочесть, состоит в том, чтобы разделить его на 2 макроса, 1 для адресации элемента массива и другой для смещения бита на место, ala BITOP(array, bit++, |=); (оба принимают BITCELL(a,b) |= BITMASK(a,b); в качестве аргумента для определения размера, но последний никогда не будет оцените a, поскольку оно появляется только в a).
    2010-07-13 09: 19: 46Z
  3. @ R .. Этот ответ действительно старый, но в этом случае я бы предпочел функцию макросу.
    2015-10-23 17: 08: 19Z
  4. Незначительный: кажется, что 3-й актерский состав sizeof существует только для того, чтобы застраховать неподписанную математику с (size_t). Может % там.
    2017-09-27 17: 58: 06Z
  5. (unsigned) может излишне сузить (size_t)(b)/(8*sizeof *(a)) перед делением. Только проблема с очень большими битовыми массивами. Все еще интересный макрос.
    2017-09-27 18: 00: 43Z

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

b

Вы должны знать о порядке упаковки битов - сначала я думаю, что это MSB, но это может зависеть от реализации. Также проверьте, как ваш компилятор обрабатывает поля, пересекающие байтовые границы.

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

    
23
2008-11-06 11: 30: 16Z
  1. Практически все, что касается битовых полей, определяется реализацией. Даже если вам удастся выяснить все детали относительно того, как ваш конкретный компилятор реализует их, использование их в вашем коде наверняка сделает его непереносимым.
    2011-08-18 19: 50: 26Z
  2. @ Lundin - Да, но битовая привязка встроенной системы (особенно в аппаратных регистрах, к которой относится мой ответ) никогда не будет полезной в любом случае переносимой.
    2011-08-19 20: 13: 25Z
  3. Возможно, не между совершенно разными процессорами. Но вы, скорее всего, хотите, чтобы он был переносимым между компиляторами и между различными проектами. И есть много встроенных «разборов», которые вообще не связаны с аппаратным обеспечением, таких как кодирование /декодирование протокола данных.
    2011-08-20 09: 35: 20Z
  4. ... и если вы привыкнете использовать битовые поля во встроенном программировании, вы обнаружите, что ваш код X86 работает быстрее и эффективнее. Не в простых тестах, где у вас есть целая машина, чтобы преодолеть тест, а в реальных многозадачных средах, где программы конкурируют за ресурсы. Преимущество CISC - его первоначальная цель заключалась в том, чтобы компенсировать процессоры быстрее, чем шины и медленную память.
    2013-02-15 22: 26: 53Z

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

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

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

    
23
2012-06-14 15: 23: 17Z
  1. Микро был Coldfire MCF52259, используя C в Codewarrior. Рассмотрение дизассемблера /ассемблера является полезным упражнением, поскольку оно показывает все шаги, которые ЦП должен пройти, чтобы выполнить даже самые основные операции. < br > Мы также заметили другие инструкции по переключению ЦП в циклах, критичных ко времени - ограничение переменной с помощью var% = max_val каждый раз обходится в кучу циклов ЦП, при этом если (var > max_val) var- = max_val использует только пару инструкций. < br > Хорошее руководство по нескольким трюкам здесь: codeproject.com/Articles/6154/…
    2012-06-19 17: 33: 18Z
  2. Еще более важно, что регистры ввода-вывода с отображением вспомогательной памяти предоставляют механизм для атомарных обновлений. Чтение /изменение /запись могут быть очень плохими, если последовательность прервана.
    2015-02-22 02: 16: 20Z
  3. Имейте в виду, что все регистры портов будут определены как
    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;
    
    , и поэтому компилятор не может выполнить какую-либо оптимизацию кода, включающего такие регистры. Поэтому рекомендуется разобрать такой код и посмотреть, как он получился на уровне ассемблера.
    2015-12-14 09: 42: 09Z

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

volatile

Пример использования:

#define bit_test(x, y)  ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )

Примечания: Это разработано, чтобы быть быстрым (учитывая его гибкость) и не ветвиться. Это приводит к эффективному машинному коду SPARC при компиляции Sun Studio 8; Я также проверил это, используя MSVC ++ 2008 на amd64. Можно сделать похожие макросы для установки и очистки битов. Ключевое отличие этого решения по сравнению со многими другими здесь заключается в том, что оно работает для любого местоположения практически во всех типах переменных.

    
19
2009-01-03 23: 44: 14Z

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

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;
}
    
19
2009-06-15 07: 38: 21Z
  1. #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 (и фактически вы делаете свой код хуже, делая это)
    2015-02-06 23: 52: 34Z

Эта программа должна изменить любой бит данных с 0 на 1 или с 1 на 0:

BITS     
13
2015-05-22 16: 07: 42Z

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

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

Обратите внимание, чтобы установить бит 'n' в 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;
}

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

    
13
2017-12-05 11:20:35Z
  1. Не используйте таблицу для функции, которая может быть реализована с помощью одного оператора. TQuickByteMask [n] эквивалентен (1 < < n). Кроме того, короткая аргументация - очень плохая идея. /И% на самом деле будет делением, а не битовым сдвигом /побитовым, и потому что знаковое деление со степенью 2 не может быть реализовано побитовым. Вы должны сделать тип аргумента unsigned int!
    2010-06-28 06: 24: 57Z
  2. Какой смысл в этом? Это только делает код медленнее и сложнее для чтения? Я не вижу в этом ни одного преимущества. 1u < < n легче читается программистами на C, и, надеюсь, может быть переведен в один тикТион. С другой стороны, ваше деление будет переведено примерно в 10 тиков, или даже так плохо, как до 100 тиков, в зависимости от того, насколько плохо конкретная архитектура справляется с делением. Что касается функции растрового изображения, то было бы более разумно иметь справочную таблицу, переводящую каждый битовый индекс в байтовый индекс, для оптимизации по скорости.
    2011-08-18 19: 32: 14Z
  3. Что касается big /little endian, big endian будет отображать целые числа и необработанные данные (например, строки) одинаково: слева направо от msb до lsb на протяжении всего весь растр. Хотя little endian будет отображать целые числа слева направо как 7-0, 15-8, 23-18, 31-24, но необработанные данные все еще остаются слева направо от msb до lsb. Так что, как мало endian лучше для вашего конкретного алгоритма, полностью вне меня, похоже, все наоборот.
    2011-08-18 19: 42: 44Z
  4. @ R .. Таблица может быть полезна, если ваша платформа не может смещаться эффективно, как старые микрочипы, но, конечно, тогда деление в образце абсолютно неэффективно
    2011-11-18 11: 28: 14Z

Используйте это:

TSetBit( n, &my_int);
    
11
2015-05-22 16: 06: 03Z
  1. Ну, он использует неэффективное ветвление.
    2011-07-02 19: 33: 00Z
  2. @ asdf Задача компилятора - вывести наиболее эффективный двоичный файл, задача программиста - написать чистый код
    2015-02-06 23: 53: 56Z
  3. Это хорошая демонстрация тестирования, установки и очистки определенного бита. Однако это очень плохой подход к переключению.
    2015-02-22 02: 18: 34Z

Расширение до ответа

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

    return num;
}
: bitset     
10
2014-05-08 04: 33: 41Z

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

См. https://www.kernel.org/doc/. htmldocs /ядро-апи /ch02s03.html

#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;
}

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

    
10
2017-12-01 16: 21: 28Z

Visual C 2010 и, возможно, многие другие компиляторы имеют непосредственную поддержку встроенных битовых операций. Удивительно, но это работает, даже оператор

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
работает правильно. 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

Обратите внимание, что в этом коде нет ничего "особенного". Это немного похоже на целое число, что технически так и есть. 1-битное целое число, которое может содержать 2 значения и только 2 значения.

Однажды я использовал этот подход, чтобы найти дубликаты записей ссуды, где loan_number был ключом ISAM, используя 6-значный номер ссуды в качестве индекса в массиве битов. Слишком быстро и спустя 8 месяцев доказали, что система мэйнфреймов, с которой мы получали данные, действительно работала неправильно. ПростыеГород битовых массивов дает уверенность в их правильности очень высоко - по сравнению с поисковым подходом, например.

    
9
2019-06-08 06: 41: 49Z
  1. std :: bitset действительно реализован в виде битов большинством компиляторов
    2014-11-17 20: 03: 37Z
  2. @ galinette, согласовано. Заголовочный файл #include < bitset > является хорошим ресурсом в этом отношении. Кроме того, специальный класс vector < bool > когда вам нужно изменить размер вектора. C ++ STL, 2nd Edition, Nicolai Josuttis подробно описывает их на страницах 650 и 281 соответственно. C ++ 11 добавляет несколько новых возможностей в std :: bitset, особый интерес для меня представляет хеш-функция в неупорядоченных контейнерах. Спасибо за головы! Я собираюсь удалить свой комментарий о судорогах. Уже достаточно мусора в сети. Я не хочу добавлять к этому.
    2014-11-17 21: 08: 22Z
  3. При этом используется как минимум целый байт памяти для каждого
    //  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;
        }
    }
    
    . Может быть, даже 4 байта для установок C89, которые используют bool для реализации int
    2015-02-06 23: 55: 27Z
  4. @ MattMcNabb, вы правы. В C ++ размер типа int, необходимый для реализации логического значения, не указан стандартом. Я понял, что этот ответ был ошибочным некоторое время назад, но решил оставить его здесь, так как люди, по-видимому, находят его полезным. Для тех, кто хочет использовать биты, наиболее полезен комментарий Галинетт, как и моя библиотека битов здесь ... stackoverflow.com/a/16534995/1899861
    2015-02-12 07: 23: 47Z
  5. @ RocketRoy: Вероятно, стоит изменить предложение, которое утверждает, что это пример "битовых операций".
    2015-02-22 02: 20: 04Z

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

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

    
6
2012-07-05 22: 58: 36Z
  1. ? - это префикс для литерала в шестнадцатеричном, а не двоичном формате.
    2015-02-22 02: 20: 34Z

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

0x     
5
2015-02-06 23: 11: 18Z

Используемая переменная

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)

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

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

int value, pos;

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

value = value | 1 << pos;

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

value = value & ~(1 << pos); 
    
5
2019-06-08 06: 38: 38Z
  

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

Чтобы устранить распространенную ошибку кодирования при попытке сформировать маску:

value = value ^ 1 << pos;
не всегда достаточно широк

Какие проблемы бываютn, если 1 является более широким типом, чем number?
1 может быть слишком велико для сдвига x, приводящего к неопределенному поведению (UB). Даже если 1 << x не слишком велико, x может не перевернуть достаточно старшие значащие биты.

~

Чтобы обеспечить достаточно широкую 1:

Код может использовать

// 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
или педантически 1ull и позволить оптимизировать компилятор. (uintmax_t)1

Или приведение - что устраняет проблемы с кодированием /просмотром /обслуживанием, поддерживая приведение в соответствие и актуальность.

number |= (1ull << x);
number |= ((uintmax_t)1 << x);

Или аккуратно продвиньте

number |= (type_of_number)1 << x;
, принудительно выполнив математическую операцию, как минимум такую ​​же, как тип 1. number

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

    
4
2017-09-27 18: 18: 48Z
  1. Интересный взгляд на старый вопрос! Ни
    number |= (number*0 + 1) << x;
    
    , ни number |= (type_of_number)1 << x; не подходят для установки знакового бита типа со знаком ... На самом деле, ни number |= (number*0 + 1) << x;. Существует ли переносимый способ сделать это по позиции?
    2017-09-27 22: 27: 57Z
  2. @ chqrlie IMO, лучший способ избежать установки знакового бита и риска UB или IDB со сдвигами - это использовать типы unsigned . подписанный код с высокой переносимостью слишком сложен, чтобы быть приемлемым.
    2017-09-27 22: 33: 49Z
number |= (1ull << x);     
4
2019-06-08 06: 39: 59Z

Шаблонная версия C ++ 11 (помещается в заголовок):

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);
}
    
3
2018-02-27 20: 51: 05Z
  1. Этот код не работает. (Кроме того, почему у вас есть
    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;}
    }
    
    после определения вашей функции?)
    2018-02-10 20: 11: 47Z
  2. @ melpomene Код не сломан, я его протестировал. Ты имеешь в виду, что он не скомпилируется или результат неверный? О лишних ';' Я не помню, они действительно могут быть удалены.
    2018-02-25 15: 51: 11Z
  3. ;?
    2018-02-25 18: 25: 33Z
  4. Спасибо, что заметили, это должно было быть (variable & bits == bits)
    2018-02-27 20: 56: 01Z
  

Предположим сначала несколько вещей
((variable & bits) == bits) Целочисленное значение для выполнения побитовых операций (set, get, clear, toggle).
num = 55 0 битовая позиция для выполнения побитовых операций.

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

  1. Чтобы получить бит n = 4 из num вправо, nth, num раз. Затем выполните побитовое И n с 1.
&

Как это работает?

bit = (num >> n) & 1;

Как установить немного?

  1. Чтобы установить определенный бит числа. Сдвиг влево 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;

Как немного очистить?

  1. Сдвиг влево 1,
           0000 0001 (1 in decimal)
        <<         4 (left shift 4 times)
    -----------------
           0001 0000
         | 0011 0111 (55 in decimal)
    -----------------
        => 0001 0000 (final result)
    
    раза, т.е. n.
  2. Выполните побитовое дополнение с указанным выше результатом. Так что n-й бит становится неустановленным, а остаток бита становится установленным, т.е. 1 << n.
  3. Наконец, выполните побитовую операцию И ~ (1 << n) с вышеуказанным результатом и &. Вышеупомянутые три шага вместе можно записать как num;

 Шаги, чтобы очистить немного

num & (~ (1 << n))

Как это работает?

num &= (~(1 << n));    // Equivalent to; num = num & (~(1 << n));

Как немного переключиться?

Чтобы немного переключить, мы используем побитовый оператор XOR

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
     ~ 0001 0000
-----------------
       1110 1111
     & 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)
. Побитовый оператор XOR оценивается в 1, если соответствующие биты обоих операндов различны, в противном случае - в 0.

Что означает переключение немного, нам нужно выполнить операцию XOR с битом, который вы хотите переключить, и 1.

^

Как это работает?

  • Если бит для переключения равен 0, тогда
    num ^= (1 << n);    // Equivalent to; num = num ^ (1 << n);
    
    .
  • Если бит для переключения равен 1, тогда 0 ^ 1 => 1.
1 ^ 1 => 0

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

    
2
2019-06-10 06: 12: 46Z

Попробуйте использовать одну из этих функций на языке C, чтобы изменить n бит:

       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     ^ 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

или

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)));
}
    
- 2
2018-02-16 01: 20: 23Z
  1. 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;
    }
    
    может вызвать неопределенное поведение
    2015-02-06 23: 57: 15Z
value << n
источник размещен Вот