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

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

прочтите это: graphics.stanford.edu/~seander/bithacks.html и, когда вы освоите это, прочтите это: realtimecollisiondetection.net/blog/?p=78

ugasoft 18.09.2008 20:01

Вам также может быть интересно проверить Бит-твиддлер, Bit Twiddling Хаки и Агрегированные магические алгоритмы.

anon 05.01.2009 14:13
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2 737
2
1 289 796
26
Перейти к ответу Данный вопрос помечен как решенный

Ответы 26

Ответ принят как подходящий

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

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

number |= 1UL << n;

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

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

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

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

number &= ~(1UL << n);

Это очистит n-й бит number. Вы должны инвертировать битовую строку с помощью побитового оператора НЕ (~), а затем И.

Немного переключаюсь

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

number ^= 1UL << n;

Это переключит n-й бит number.

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

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

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

bit = (number >> n) & 1U;

Это поместит значение n-го бита number в переменную bit.

Изменение бита пth на Икс

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

number ^= (-x ^ number) & (1UL << n);

Бит n будет установлен, если x равен 1, и сброшен, если x равен 0. Если x имеет другое значение, вы получите мусор. x = !!x преобразует его в логическое значение 0 или 1.

Чтобы сделать это независимым от поведения отрицания дополнения до 2 (где -1 имеет все биты, в отличие от реализации дополнения до 1 или знака / величины C++), используйте отрицание без знака.

number ^= (-(unsigned long)x ^ number) & (1UL << n);

или же

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) установит n-й бит на x.

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

Я хотел бы отметить, что на платформах, которые имеют встроенную поддержку для установки / сброса бит (например, микроконтроллеры AVR), компиляторы часто переводят myByte | = (1 << x) 'в собственные инструкции установки / очистки бит, когда x константа, например: (1 << 5), или const без знака x = 5.

Aaron 17.09.2008 21:13

бит = число & (1 << x); не помещает значение бита x в бит, если бит не имеет типа _Bool (<stdbool.h>). В противном случае бит = !! (число & (1 << x)); буду..

Chris Young 16.11.2008 10:49

Подобные решения работают только в том случае, если целевая переменная имеет целочисленный тип. Могут быть приняты более общие решения для работы с другими полезными целевыми типами, такими как массивы.

John Zwinck 04.01.2009 01:55

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

Happy Green Kid Naps 09.05.2013 18:27

Кстати, бит-игра здесь будет тихо терпеть неудачу, если number шире, чем int.

anatolyg 09.05.2013 19:58

почему бы тебе не поменять последнюю на bit = (number >> x) & 1

aaronman 26.06.2013 22:47

@Aaron: x86 имеет собственный битовый тест, а также инструкции по установке / очистке / дополнению

phuclv 01.08.2013 06:04

Думаю, проблема в обозначениях. Скажем, у меня есть 0011 (десятичное 3), и я хочу проверить, установлен ли бит второй, например, тот, который выделен жирным шрифтом: 0 0 1 1. Как вы относитесь к x? Будет ли это 2-й или 1-й бит в соответствии с вашей записью, потому что, если это 2-й бит, я думаю, ваше предложение не сработает, например, поскольку вы сдвинете 1 два раза и получите 100 - что не даст 2-й бит как Я определил выше. Не так ли?

user2568508 14.09.2013 01:40

1 - это подписанный литерал int. Таким образом, все операции здесь работают с числами со знаком, что не очень хорошо определено стандартами. Стандарты не гарантируют дополнение до двух или арифметический сдвиг, поэтому лучше использовать 1U.

Siyuan Ren 10.12.2013 12:53

Чтобы прояснить комментарий анатолика, константа «1» без модификаторов определяется как int со знаком. Чтобы это работало правильно для всех переменных, используйте вместо этого «1ULL».

JonS 08.07.2014 02:46

@JonS для всех переменных размером до unsigned long long в любом случае ... могут быть расширения, определяемые реализацией, такие как __int128. Чтобы быть сверхбезопасным (uintmax_t)1 << x

M.M 07.02.2015 02:48

Я предпочитаю number = number & ~(1 << n) | (x << n); для изменения n-го бита на x.

jiasli 24.03.2015 03:38

@Jiashuo Li: этот оператор завершится ошибкой, если number больше int, а n больше или равен количеству битов в int. В этом случае он даже вызывает неопределенное поведение. number = number & ~((uintmax_t)1 << n) | ((uintmax_t)x << n); - это общее выражение, которое должно работать для всех размеров number, но может генерировать уродливый и неэффективный код для меньших размеров.

chqrlie 24.03.2015 11:48

Немного по поводу проверки: почему бы просто не использовать "number & 0x01" для проверки первого бита, "number & 0x08" для четвертого и т. д. Imho более красивое.

Anonymous 08.04.2015 14:28

Использование таких операций, как -x, небезопасно, поскольку стандарт C позволяет целым числам со знаком быть (например) величиной знака, дополнением до единиц или любой другой системой, которая может выражать необходимый диапазон, что означает, что -x - это нет, который должен быть таким же, как (~x) + 1. . Это не так уж важно для современных архитектур, но вы никогда не знаете, что достаточно умный оптимизирующий компилятор сделает с вашим кодом.

Kevin 18.01.2016 21:04

Почему мы используем ~ вместо !?

71GA 30.01.2017 10:12

@ Анонимный. Мы все равно используем маски, так что если нам когда-нибудь понадобится сделать более одного расширения, очевидно? Примечание: верхний / нижний регистр в EBCDIC составляет 1 бит.

mckenzm 09.03.2017 05:19

Разве последний пример не предполагает представление с дополнением до двух? Разве это не плохо / непереносимо, как бы вы это ни называли?

Patrick Roberts 15.06.2017 00:46

@ 71GA: Побитовое отрицание (~) инвертирует все биты, поэтому ~0xFFF0FFFF - это 0x000F0000. Логическое значение not (!) дает 0, если значение не равно нулю, или 1, если значение равно нулю, поэтому !0xFFF0FFFF - это 0x00000000.

Adrian McCarthy 05.09.2017 23:34

@PatrickRoberts: Да, это требует дополнения до двух. В дополнение, -1 - это 0b1111..1110. (Все единицы - -0). C++ также допускает целочисленные представления знака / величины, где -x просто меняет знаковый бит. Я обновил этот ответ, чтобы указать на это. Совершенно нормально, если вы ориентируетесь только на две дополнительные реализации C++. Это не UB, это просто определяется реализацией, поэтому для правильной работы требуется определить целые числа со знаком как дополнение до 2. Я также изменил константы на 1UL, отметив, что может потребоваться 1ULL.

Peter Cordes 10.11.2017 22:55

@Kevin: Это гарантированно безопасно для реализации C++, в которой используется дополнение 2. Это «определяется реализацией», а не UB. Следует беспокоиться о переносимости до дополнения или знака / величины, а не об оптимизации компиляторов.

Peter Cordes 10.11.2017 22:57

@Peter -x - это UB, если x - INT_MIN.

Kevin 10.11.2017 23:55

@Kevin: да ладно, должен быть -(unsigned long)x, который также обошел бы целочисленное представление со знаком. (беззнаковое основание 2 соответствует семантике дополнения до 2). Но это работает правильно, только если x равен 0 или 1. Помните, что мы устанавливаем бит n на x.

Peter Cordes 11.11.2017 00:05

@PeterCordes: Это вполне разумно. Но когда разработчик говорит мне, что «этот инвариант будет удерживаться всегда!» Я очень нервничаю.

Kevin 11.11.2017 00:18

@Kevin: Я решил опустить любое упоминание о подписанном переполнении UB в моем последнем редактировании, потому что вы все равно хотите использовать unsigned для портативного 0U - 1U -> all-one. Как это выглядит сейчас? Я постарался, чтобы ответ был простым. Я уже упоминал, что вам может потребоваться логическое преобразование с !!x. Если бы это был мой собственный ответ, я мог бы вставить больше текста о том, что всегда использовать unsigned, но я просто сохраняю этот старый канонический ответ. (Джереми, надеюсь, вам понравятся изменения, возможно, вы захотите отредактировать, чтобы изложить мои изменения своими словами или что-то еще, что вы хотите сказать почти 9 лет спустя.)

Peter Cordes 11.11.2017 00:21

В истории редактирования только что заметил, что изменение n-го бита в раздел x было добавлено другим сторонним редактированием и не было работой Джереми.

Peter Cordes 11.11.2017 00:26

Является ли ваш метод изменения n-го бита на x действительным и для C90? И справедливо ли это для чисел без знака?

avivgood2 22.04.2020 11:37

Я сталкивался с этим довольно редко и всегда думал, что мне нужно придумать одно выражение для выполнения операции set bit to value. Обычно я использую save state + unconditionally set/clear + restore state аппаратно. Во всяком случае, я случайно наткнулся на это и обязательно украду выражение выше.

sherrellbc 15.01.2021 16:46

number = (number | (1UL << n)) ^ (!x << n) Упрощено для удаления логического «нет» и побитового «нет».

Shogan Aversa-Druesne 18.01.2021 07:02

Вышеупомянутое выражение ^^^^^ позволяет x быть неограниченным, x может быть 0 или любым положительным числом для истины и не привязан к 0 и 1

Shogan Aversa-Druesne 18.01.2021 07:09

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

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

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

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

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

В остальном я поддерживаю решение Джереми.

В качестве альтернативы вы можете сделать функцию clearbits() вместо &= ~. Почему вы используете для этого перечисление? Я думал, что они предназначены для создания группы уникальных переменных со скрытым произвольным значением, но вы присваиваете определенное значение каждой из них. Так в чем же преимущество по сравнению с простым определением их как переменных?

endolith 20.12.2011 19:09

@endolith: Использование enum для наборов связанных констант имеет долгую историю в программировании на языке C. Я подозреваю, что у современных компиляторов единственное преимущество перед const short или чем-то еще в том, что они явно сгруппированы вместе. И когда они нужны для чего-то Другой, а не для битовых масок, вы получаете автоматическую нумерацию. В С ++, конечно, они также образуют отдельные типы, что дает вам небольшую дополнительную статическую проверку ошибок.

dmckee --- ex-moderator kitten 22.12.2011 05:15

Вы попадете в неопределенные константы перечисления, если не определите константу для каждого из возможных значений битов. Например, какое значение enum ThingFlags для ThingError|ThingFlag1?

Luis Colorado 30.09.2014 14:55

Если вы используете этот метод, имейте в виду, что константы перечисления всегда имеют подписанный тип int. Это может вызвать всевозможные тонкие ошибки из-за неявного целочисленного продвижения или побитовых операций над знаковыми типами. thingstate = ThingFlag1 >> 1, например, вызовет поведение, определяемое реализацией. thingstate = (ThingFlag1 >> x) << y может вызывать неопределенное поведение. И так далее. На всякий случай всегда приводите к беззнаковому типу.

Lundin 14.12.2015 12:25

@Lundin: Начиная с C++ 11, вы можете установить базовый тип перечисления, например: enum My16Bits: unsigned short { ... };

Aiken Drum 15.03.2016 18:01

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

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

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

Я всегда считал, что использование битовых полей - плохая идея. У вас нет контроля над порядком, в котором распределяются биты (сверху или снизу), что делает невозможным сериализацию значения стабильным / переносимым способом, кроме побитового. Также невозможно смешивать битовую арифметику DIY с битовыми полями, например, создать маску, которая проверяет сразу несколько битов. Конечно, вы можете использовать && и надеяться, что компилятор правильно его оптимизирует ...

R.. GitHub STOP HELPING ICE 28.06.2010 10:17

Битовые поля настолько плохи во многих отношениях, что я мог бы написать об этом книгу. Фактически, мне почти пришлось сделать это для небольшой полевой программы, которая требовала соответствия MISRA-C. MISRA-C предписывает документировать все поведение, определяемое реализацией, поэтому в итоге я написал эссе обо всем, что может пойти не так в битовых полях. Порядок битов, порядок байтов, биты заполнения, байты заполнения, различные другие проблемы с выравниванием, неявные и явные преобразования типов в битовое поле и из него, UB, если int не используется, и так далее. Вместо этого используйте побитовые операторы для уменьшения количества ошибок и переносимого кода. Битовые поля полностью избыточны.

Lundin 18.08.2011 23:19

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

Ferruccio 18.08.2011 23:35

Значит, порядок битов произвольный, и вы не можете использовать его для вращения отдельных битов в микроконтроллере?

endolith 08.03.2012 23:47

@endolith: Это было бы плохой идеей. Вы могли бы заставить его работать, но это не обязательно будет переносимым на другой процессор, или на другой компилятор, или даже на следующую версию того же компилятора.

Ferruccio 09.03.2012 01:02

Это круто. Мой вопрос: после проверки sizeof(mybits) я получаю 12 (т.е. размер трех ints). Это место, выделенное в памяти, или какая-то ошибка в функции sizeof?

iGbanam 29.11.2012 02:39

@Yasky - какой компилятор вы используете? Я получаю 4 с VC++ 11 и Clang 3.1.

Ferruccio 29.11.2012 04:36

@Ferruccio Я использую gcc версии 4.4.5

iGbanam 29.11.2012 05:02

@Р. Можно использовать оба, struct может быть помещен в (обычно анонимный) union с целым числом и т. д. Это работает. (Я понимаю, что это старый тред, кстати)

Shade 17.05.2014 08:59

@Shade Нет никаких гарантий, что биты битового поля отображаются разумным и предсказуемым образом с другими типами данных в том же объединении. Весь такой код в лучшем случае будет полностью непереносимым.

Lundin 14.12.2015 12:30

@Yasky и Ferruccio, получающие разные ответы на sizeof () для этого подхода, должны проиллюстрировать проблемы с совместимостью не только между компиляторами, но и между оборудованием. Иногда мы обманываем себя тем, что решили эти проблемы с помощью языков или определенных сред выполнения, но на самом деле все сводится к тому, «будет ли это работать на моей машине?». Вы, внедренные ребята, заслужите мое уважение (и сочувствие).

Kelly S. French 08.12.2016 19:11

вы можете захотеть __attribute__((packed))

user13709754 28.06.2020 03:57

Из файла bitops.h snip-c.zip:

/*
**  Bit set, clear, and test operations
**
**  public domain snippet by Bob Stout
*/

typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;

#define BOOL(x) (!(!(x)))

#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))

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

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

0000 0000 0000 0000 0000 0000 0000 0001 binary.

Если posn == 8, он будет оценивать как:

0000 0000 0000 0000 0000 0001 0000 0000 binary.

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

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

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

Не уверены? Вот тестовый код - я использовал 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

---- [финиш] ------------------------------------------- ----------------------

2 вещи об этом: (1) при просмотре ваших макросов некоторые могут ошибочно полагать, что макросы на самом деле устанавливают / очищают / переворачивают биты в arg, однако нет никакого присваивания; (2) ваш test.c не завершен; Я подозреваю, что если вы запустите больше случаев, вы обнаружите проблему (упражнение для читателя)

Dan 18.10.2008 05:51

-1 Это просто странная обфускация. Никогда не изобретайте язык C заново, скрывая синтаксис языка за макросами, это плохая практика очень. Затем некоторые странности: сначала 1L подписан, то есть все битовые операции будут выполняться над подписанным типом. Все, что передается в эти макросы, будет возвращено как подписанное. Фигово. Во-вторых, это будет работать очень неэффективно на меньших ЦП, так как будет выполняться долго, когда операции могли быть на уровне int. В-третьих, макросы, подобные функциям, являются корнем всех зол: у вас вообще нет типобезопасности. Кроме того, очень верен предыдущий комментарий об отсутствии назначения.

Lundin 18.08.2011 23:14

Это не удастся, если arg - long long. 1L должен быть максимально широким типом, поэтому (uintmax_t)1. (Вам может сойти с рук 1ull)

M.M 07.02.2015 02:51

Вы оптимизировали размер кода? На основных процессорах Intel вы получите остановку частичного регистра при чтении AX или EAX после того, как эта функция вернется, потому что она записывает 8-битные компоненты EAX. (Это нормально для процессоров AMD или других, которые не переименовывают частичные регистры отдельно от полного регистра. Haswell / Skylake не переименовывают AL отдельно, но они переименовывают AH.).

Peter Cordes 11.11.2017 00:38

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

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

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

Не используйте таблицу для функции, которая может быть реализована с помощью одного оператора. TQuickByteMask [n] эквивалентен (1 << n). Кроме того, очень плохая идея - делать ваши аргументы краткими. На самом деле / ​​и% будут делением, а не побитовым сдвигом / побитовым и, поскольку знаковое деление на степень 2 не может быть реализовано побитовым. Вы должны сделать тип аргумента беззнаковым int!

R.. GitHub STOP HELPING ICE 28.06.2010 10:24

Какой в ​​этом смысл? Это только делает код медленнее и труднее для чтения? Я не вижу в этом ни единого преимущества. 1u << n легче читать программистам на C, и мы надеемся, что его можно преобразовать в инструкцию ЦП с одним тактом. С другой стороны, ваше деление будет преобразовано примерно в 10 тиков или даже до 100 тиков, в зависимости от того, насколько плохо конкретная архитектура обрабатывает деление. Что касается функции растрового изображения, было бы разумнее иметь таблицу поиска, переводящую каждый битовый индекс в байтовый индекс для оптимизации скорости.

Lundin 18.08.2011 23:32

Что касается big / little endian, big endian будет отображать целые числа и необработанные данные (например, строки) таким же образом: слева направо от msb до lsb по всему растровому изображению. В то время как little endian будет отображать целые числа слева направо как 7-0, 15-8, 23-18, 31-24, но сырые данные по-прежнему идут слева направо от msb до lsb. Так что, насколько меньший порядок байтов лучше для вашего конкретного алгоритма, я полностью не понимаю, кажется, наоборот.

Lundin 18.08.2011 23:42

@R .. Таблица может быть полезна, если ваша платформа не может эффективно сдвигаться, как старые микрочипы микрочипа, но, конечно, тогда деление в образце абсолютно неэффективно

jeb 18.11.2011 15:28

Использование стандартной библиотеки C++: 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;
}

[Alpha:] > ./a.out
00010

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

+1. Не то, чтобы std :: bitset можно было использовать с "C", но поскольку автор пометил свой вопрос с помощью "C++", AFAIK, ваш ответ здесь лучший ... std :: vector <bool> - другой способ, если знать его плюсы и минусы

paercebal 19.09.2008 22:16

Отлично, Мартин! Вы даже можете использовать перечисление для «индексации» битов: enum {cEngineOn, cDoorsOpen, cAircoOn}; std :: bitset <cNBBITS> mybits; mybits [cEngineOn] .set (); const bool cbDoorOpen = mybits [cDoorsOpen]; ...

xtofl 27.09.2008 23:21

среди ответов здесь я думаю, что это лучший способ управлять битами ... но я думаю, что суть вопроса заключалась в том, как манипулировать битами вручную. еще, + голосование :)

moogs 15.10.2008 15:43

@paercebal: vector <bool> не очень эффективен, так как bool в C++ занимает полный байт пространства вместо 1 бита ...

andrewdotnich 13.11.2008 09:10

@andrewdotnich: vector <bool> - это (к сожалению) специализация, которая хранит значения в виде битов. См. gotw.ca/publications/mill09.htm для получения дополнительной информации ...

Niklas 12.12.2008 23:40

Возможно, никто не упомянул об этом, потому что это было помечено как встроенное. В большинстве встроенных систем вы избегаете STL как чумы. И поддержка ускорения, вероятно, очень редкая птица, которую можно обнаружить среди большинства встраиваемых компиляторов.

Lundin 18.08.2011 23:47

@Lundin: Первая точка неверна (некоторые вещи в STL избегаются, но такой общий оператор, как это, является общим, std :: bitset прекрасен и ничего не стоит использовать). Точка два Boost :: dynamic_bitset ни от чего не зависит и легко может использоваться.

Martin York 18.08.2011 23:51

@Martin Это правда. Помимо определенных убийц производительности, таких как STL и шаблоны, многие встроенные системы даже полностью избегают использования стандартных библиотек, потому что их очень сложно проверить. Большая часть встраиваемой ветви охватывает такие стандарты, как MISRA, для которых требуются инструменты статического анализа кода (кстати, такие инструменты должны использовать любые профессионалы в области программного обеспечения, а не только встроенные специалисты). Обычно у людей есть дела поважнее, чем запускать статический анализ через всю стандартную библиотеку - если ее исходный код даже доступен для них в конкретном компиляторе.

Lundin 19.08.2011 10:26

@Lundin: Ваши утверждения слишком широки (поэтому спорить бесполезно). Я уверен, что могу найти ситуации, в которых они правдивы. Это не меняет моей исходной точки. Оба эти класса идеально подходят для использования во встроенных системах (и я точно знаю, что они используются). Ваша первоначальная точка зрения о том, что STL / Boost не используется во встроенных системах, также неверна. Я уверен, что есть системы, которые их не используют, и даже системы, которые их используют, они используются разумно, но говорить, что они не используются, просто неверно (потому что есть системы, в которых они используются).

Martin York 19.08.2011 10:41

@ jons34yp: Документация SGI более коническая и обычно содержит меньше ошибок, чем cppreference.

Martin York 24.04.2013 01:13

В теме четко указан язык C, нет C++. Ясно, что это неправильный ответ, так как же он получил 120+ голосов ?!

mloskot 10.09.2013 16:24

@mloskot: В вопросе четко говорится о C++. Его в теге под вопросом, какие языки допустимы. Он получил 120+ голосов, потому что люди, которые умеют читать, читают вопрос и правильно оценивают его в контексте тегов. :-) Ох. А это просто.

Martin York 11.09.2013 01:42

@LokiAstari Осмелюсь заявить, что либо тег неверен, либо тело темы и неточно. Это важный вопрос, так как он приводит к тому, что ТАК вопросы становятся расплывчатыми, а значит, на самом деле бесполезными.

mloskot 11.09.2013 13:14

Все, что я знаю, это то, что каждый раз, когда я пытаюсь скомпилировать что-то, сделанное на C++, Boost становится моим новым злейшим врагом. Все равно проголосовали за.

sudo 13.08.2014 11:14

@ 9000: Почему? Вы не должны ничего делать с наддувом. Это библиотека только для заголовков, и установка ускорения должна быть связана с управлением пакетами sudo apt-get install boost-devel

Martin York 13.08.2014 13:09

Вопрос задан для C / C++, поэтому, чтобы быть совместимым с обоими мирами, я думаю, что STL здесь неприменим.

Luis Colorado 30.09.2014 14:53

@LuisColorado: Но не говорить об этом - значит не иметь доступ к информации для пользователей. Дело не в том, чтобы ограничить ваш ответ, а в том, чтобы предложить решение. ОП решит, актуально ли это, поставив галочку рядом с лучшим ответом. Вы голосуете «за», если считаете, что это полезный для сообщества ответ, или «против», если он не имеет ценности для сообщества.

Martin York 30.09.2014 18:47

@LuisColorado: Я не согласен. Это решило проблему способом, о котором не упоминали другие. По общему признанию, это было для части людей, которые использовали C++, но вопрос был помечен как C++ и, следовательно, правильный ответ.

Martin York 02.10.2014 21:55

@LokiAstari, и по этой причине я только что сделал комментарий, без отрицательного голоса, ничего больше ... просто комментарий.

Luis Colorado 05.10.2014 14:18

@MartinYork Мир встроенных систем огромен; но если избегать STL и вместо этого некоторые контейнеры и т. д. написаны самостоятельно, код почти наверняка будет менее производительным и будет содержать больше ошибок. (Я работал над одной такой системой в начале 2000-х. Она была хорошо встроена, информационно-развлекательная, но 5 лет назад у нее были ресурсы ПК.)

Peter - Reinstate Monica 22.01.2020 20:15

@ Peter-ReinstateMonica, только здесь мы говорим о std::bitset.

Martin York 22.01.2020 22:04

@MartinYork Я пытался поддержать ваше заявление; "STL / Boost, который не используется во встроенных системах, тоже неверен" - это правильно. Это используется. (А иногда, когда система достаточно функциональна, а программное обеспечение достаточно сложное, на самом деле это ошибка, чтобы не использовать STL, потому что альтернативы - без дженериков или самодельные - хуже.)

Peter - Reinstate Monica 22.01.2020 23:13

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

/* a=target variable, b=bit number to act upon 0-n */
#define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b))))        // '!!' to make sure this returns 0 or 1

/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK_ALL(x,y) (!(~(x) & (y)))
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))

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

Robert Kelly 02.10.2013 18:53

BITMASK_CHECK(x,y) ((x) & (y)) должен быть ((x) & (y)) == (y), иначе он вернет неверный результат по многобитной маске (например, 5 vs. 3) / * Привет всем могильщикам :) * /

brigadir 11.12.2014 15:00

1 должен быть (uintmax_t)1 или аналогичным на тот случай, если кто-то попытается использовать эти макросы на long или более крупном типе.

M.M 07.02.2015 02:50

Или 1ULL работает так же хорошо, как (uintmax_t) на большинстве реализаций.

Peter Cordes 11.11.2017 00:27

@brigadir: зависит от того, хотите ли вы проверить наличие каких-либо установленных битов или всех установленных битов. Я обновил ответ, включив оба с описательными именами.

Peter Cordes 11.11.2017 00:31

BITMASK_CHECK_ALL(x,y) может быть реализован как !~((~(y))|(x))

Handy999 20.11.2018 12:24

@ Handy999 Немного легче понять, почему это работает, после применения закона Де Моргана и перестройки, чтобы получить !(~(x) & (y)).

Tavian Barnes 13.08.2019 23:14

Это было настолько полезно, что я удивляюсь, почему я не пробовал это раньше, особенно #define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b)))) // '!!' to make sure this returns 0 or 1.

William Martens 16.02.2021 19:12

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

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

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

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

Lundin 18.08.2011 23:50

@Lundin - Верно, но использование встроенной системы в битах (особенно в аппаратных регистрах, к чему и относится мой ответ) в любом случае никогда не будет полезно переносимым.

Roddy 20.08.2011 00:13

Возможно, не между совершенно разными процессорами. Но вы, скорее всего, захотите, чтобы его можно было переносить между компиляторами и между разными проектами. И есть много встроенных "битов", которые вообще не связаны с оборудованием, например, кодирование / декодирование протокола данных.

Lundin 20.08.2011 13:35

... и если вы привыкнете использовать битовые поля для встроенного программирования, вы обнаружите, что ваш код X86 работает быстрее и компактнее. Не в простых тестах, где у вас есть вся машина, чтобы превзойти тест, а в реальных многозадачных средах, где программы конкурируют за ресурсы. Преимущество CISC - первоначальная цель которого заключалась в том, чтобы компенсировать использование ЦП быстрее, чем шины, и медленной памяти.

user1899861 16.02.2013 02:26

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

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

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

int main(void)
{
    unsigned char arr[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };

    for (int ix = 0; ix < 64; ++ix)
        printf("bit %d is %d\n", ix, bit_test(arr, ix));

    return 0;
}

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

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

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

    return num;
}

Что ж, он использует неэффективное ветвление.

asdf 02.07.2011 23:33

@asdf Задача компилятора - выводить наиболее эффективный двоичный файл, работа программиста - написать понятный код.

M.M 07.02.2015 02:53

Это хорошая демонстрация тестирования, установки и очистки определенного бита. Однако это очень плохой подход для небольшого переключения.

Ben Voigt 22.02.2015 05:18

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

#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 (и на самом деле вы сделаете свой код хуже, сделав это)

M.M 07.02.2015 02:52

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

foraidt 13.07.2010 12:27

В самом деле. =) Один из вариантов, который вы могли бы предпочесть, - разделить его на 2 макроса, один для адресации элемента массива, а другой для смещения бита на место, например, BITCELL(a,b) |= BITMASK(a,b); (оба принимают a в качестве аргумента для определения размера, но последний никогда не будет оцените a, поскольку он появляется только в sizeof).

R.. GitHub STOP HELPING ICE 13.07.2010 13:19

@R .. Этот ответ действительно старый, но в этом случае я бы предпочел функцию макросу.

PC Luddite 23.10.2015 20:08

Незначительное: кажется, что третий (size_t) нужен только для того, чтобы подстраховать беззнаковая математика с %. Мог (unsigned) там.

chux - Reinstate Monica 27.09.2017 20:58

(size_t)(b)/(8*sizeof *(a)) без необходимости может сузить b до разделения. Только проблема с очень большими битовыми массивами. Еще интересный макрос.

chux - Reinstate Monica 27.09.2017 21:00

Эта программа предназначена для изменения любого бита данных с 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);
        }
    }
}

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

Пример:

value is 0x55;
bitnum : 3rd.

Оператор & используется для проверки бита:

0101 0101
&
0000 1000
___________
0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)

Переключить или перевернуть:

0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)

Оператор |: установить бит

0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)

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

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

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

Micro - это Coldfire MCF52259, использующий C в Codewarrior. Рассмотрение дизассемблера / asm - полезное упражнение, поскольку оно показывает все шаги, которые должен пройти ЦП, чтобы выполнить даже самую простую операцию. <br> Мы также заметили другие инструкции по загрузке ЦП в критических по времени циклах - ограничение переменной, выполняя var% = max_val, каждый раз требует кучи циклов ЦП, тогда как выполнение if (var> max_val) var- = max_val использует только пара инструкций. <br> Хорошее руководство по еще нескольким трюкам здесь: codeproject.com/Articles/6154/…

John U 19.06.2012 21:33

Что еще более важно, вспомогательные регистры ввода-вывода с отображением в память обеспечивают механизм атомарных обновлений. Чтение / изменение / запись могут произойти очень плохо, если последовательность прервана.

Ben Voigt 22.02.2015 05:16

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

Lundin 14.12.2015 12:42

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 упрощают настройку и очистку bool.

Чтобы найти непечатаемые символы:

//  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 значения.

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

std :: bitset действительно реализован в виде бит большинством компиляторов.

galinette 17.11.2014 23:03

@galinette, согласен. Заголовочный файл #include <bitset> является хорошим ресурсом в этом отношении. Также есть специальный класс vector <bool> на тот случай, когда вам нужно изменить размер вектора. C++ STL, 2-е издание, Николай М. Йозаттис исчерпывающе описывает их на страницах 650 и 281 соответственно. C++ 11 добавляет несколько новых возможностей в std :: bitset, особый интерес для меня представляет хеш-функция в неупорядоченных контейнерах. Спасибо за внимание! Я собираюсь удалить свой комментарий о спазме мозга. В сети уже достаточно мусора. Я не хочу ничего к этому добавлять.

user1899861 18.11.2014 00:08

Это использует как минимум целый байт памяти для каждого bool. Возможно, даже 4 байта для настроек C89, которые используют int для реализации bool

M.M 07.02.2015 02:55

@MattMcNabb, вы правы. В C++ размер типа int, необходимый для реализации логического значения, не указан стандартом. Некоторое время назад я понял, что этот ответ был ошибочным, но решил оставить его здесь, поскольку люди, по-видимому, находят его полезным. Для тех, кто хочет использовать биты, комментарий Галинетт наиболее полезен, как и моя битовая библиотека здесь ... stackoverflow.com/a/16534995/1899861

user1899861 12.02.2015 10:23

@RocketRoy: Тогда, наверное, стоит изменить предложение, в котором говорится, что это пример "битовых операций".

Ben Voigt 22.02.2015 05:20

Расширяя ответ bitset:

#include <iostream>
#include <bitset>
#include <string>

using namespace std;
int main() {
  bitset<8> byte(std::string("10010011");

  // Set Bit
  byte.set(3); // 10010111

  // Clear Bit
  byte.reset(2); // 10010101

  // Toggle Bit
  byte.flip(7); // 00010101

  cout << byte << endl;

  return 0;
}

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

char bitfield;

// Start at 0th position

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}

Или же

void chang_n_bit(int n, int value)
{
    bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}

Или же

void chang_n_bit(int n, int value)
{
    if (value)
        bitfield |= 1 << n;
    else
        bitfield &= ~0 ^ (1 << n);
}

char get_n_bit(int n)
{
    return (bitfield & (1 << n)) ? 1 : 0;
}

value << n может вызвать неопределенное поведение

M.M 07.02.2015 02:57

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

SET_FLAG(Status, Flag)            ((Status) |= (Flag))
CLEAR_FLAG(Status, Flag)          ((Status) &= ~(Flag))
INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
TEST_FLAGS(t,ulMask, ulBit)       (((t)&(ulMask)) == (ulBit))
IS_FLAG_SET(t,ulMask)             TEST_FLAGS(t,ulMask,ulMask)
IS_FLAG_CLEAR(t,ulMask)           TEST_FLAGS(t,ulMask,0)

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

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

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

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

How do you set, clear, and toggle a single bit?

Чтобы устранить распространенную ошибку кодирования при попытке сформировать маску:
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); не подходит. Есть ли портативный способ сделать это по позиции?

chqrlie 28.09.2017 01:27

@chqrlie IMO, лучший способ избежать установки знакового бита и риска для UB или IDB со сдвигами - использовать типы беззнаковый. Сильно переносимый код сдвига подписанный слишком запутан, чтобы быть приемлемым.

chux - Reinstate Monica 28.09.2017 01:33

Шаблонная версия (помещается в файл заголовка) с поддержкой изменения нескольких бит (работает на микроконтроллерах AVR, кстати):

namespace bit {
  template <typename T1, typename T2>
  constexpr inline T1 bitmask(T2 bit) 
  {return (T1)1 << bit;}
  template <typename T1, typename T3, typename ...T2>
  constexpr inline T1 bitmask(T3 bit, T2 ...bits) 
  {return ((T1)1 << bit) | bitmask<T1>(bits...);}

  /** Set these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void set (T1 &variable, T2 ...bits) 
  {variable |= bitmask<T1>(bits...);}
  /** Set only these bits (others will be cleared) */
  template <typename T1, typename ...T2>
  constexpr inline void setOnly (T1 &variable, T2 ...bits) 
  {variable = bitmask<T1>(bits...);}
  /** Clear these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void clear (T1 &variable, T2 ...bits) 
  {variable &= ~bitmask<T1>(bits...);}
  /** Flip these bits (others retain their state) */
  template <typename T1, typename ...T2>
  constexpr inline void flip (T1 &variable, T2 ...bits) 
  {variable ^= bitmask<T1>(bits...);}
  /** Check if any of these bits are set */
  template <typename T1, typename ...T2>
  constexpr inline bool isAnySet(const T1 &variable, T2 ...bits) 
  {return variable & bitmask<T1>(bits...);}
  /** Check if all these bits are set */
  template <typename T1, typename ...T2>
  constexpr inline bool isSet (const T1 &variable, T2 ...bits) 
  {return ((variable & bitmask<T1>(bits...)) == bitmask<T1>(bits...));}
  /** Check if all these bits are not set */
  template <typename T1, typename ...T2>
  constexpr inline bool isNotSet (const T1 &variable, T2 ...bits) 
  {return ((variable & bitmask<T1>(bits...)) != bitmask<T1>(bits...));}
}

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

#include <iostream>
#include <bitset> // for console output of binary values

// and include the code above of course

using namespace std;

int main() {
  uint8_t v = 0b1111'1100;
  bit::set(v, 0);
  cout << bitset<8>(v) << endl;

  bit::clear(v, 0,1);
  cout << bitset<8>(v) << endl;

  bit::flip(v, 0,1);
  cout << bitset<8>(v) << endl;

  bit::clear(v, 0,1,2,3,4,5,6,7);
  cout << bitset<8>(v) << endl;

  bit::flip(v, 0,7);
  cout << bitset<8>(v) << endl;
}

Кстати: оказывается, что constexpr и inline не используются, если не отправлять аргумент оптимизатора (например: -O3) компилятору. Не стесняйтесь попробовать код по адресу https://godbolt.org/ и посмотреть на вывод ASM.

Этот код не работает. (Кроме того, почему у вас стоит ; после определений функций?)

melpomene 10.02.2018 23:11

@melpomene Код не битый, тестировал. Вы имеете в виду, что он не будет компилироваться или результат неправильный? О дополнительном ';' Не помню, их действительно можно убрать.

Joakim L. Christiansen 25.02.2018 18:51

(variable & bits == bits)?

melpomene 25.02.2018 21:25

Спасибо, что обратили внимание, это должен был быть ((variable & bits) == bits)

Joakim L. Christiansen 27.02.2018 23:56

использовать std::bitsetin C++ 11

pqnet 25.10.2019 23:27

К сожалению, вы даже не можете увидеть код без прокрутки в сторону, так как он очень многословен. Возможно, поможет несколько символов новой строки?

Asteroids With Wings 22.04.2020 19:53

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.

Xeverous 05.05.2020 19:07

@Xeverous да, это зависит от намерения вызывающего абонента

Sazzad Hissain Khan 05.05.2020 21:09

Let suppose few things first
num = 55 Integer to perform bitwise operations (set, get, clear, toggle).
n = 4 0 based bit position to perform bitwise operations.

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

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

Steps to clear a bit

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)

Рекомендуемая литература - Упражнения с побитовым оператором

Спасибо за подробное объяснение. Вот ссылка на практическую задачу для BIT Magic связь

Chandra Shekhar 28.12.2019 21:37

Эта программа основана на вышеупомянутом решении @ 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

Установка n-го бита в x (значение бита) без использования -1

Иногда, когда вы не уверены, к чему приведет -1 или что-то подобное, вы можете установить n-й бит без использования -1:

number = (((number | (1 << n)) ^ (1 << n))) | (x << n);

Объяснение: ((number | (1 << n) устанавливает n-й бит в 1 (где | означает побитовое ИЛИ), затем с (...) ^ (1 << n) мы устанавливаем n-й бит в 0 и, наконец, с (...) | x << n) мы устанавливаем n-й бит, который был равен 0, в (значение бита) x.

Это также работает в golang.

Другие вопросы по теме