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

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


person JeffV    schedule 07.09.2008    source источник


Ответы (27)


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

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

number |= 1UL << n;

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

Используйте 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.

Изменение n -го бита на x

Установка 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);

or

unsigned long newbit = !!x;    // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);

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

or

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

(number & ~(1UL << n)) очистит n-й бит, а (x << n) установит n-й бит в x.

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

person Paige Ruten    schedule 07.09.2008
comment
Я хотел бы отметить, что на платформах, которые имеют встроенную поддержку установки / сброса битов (например, микроконтроллеры AVR), компиляторы часто переводят myByte | = (1 ‹< x) в собственные инструкции по установке / сбросу битов всякий раз, когда x константа, например: (1 ‹‹ 5), или const без знака x = 5. - person Aaron; 17.09.2008
comment
бит = число & (1 ‹< x); не помещает значение бита x в бит, если бит не имеет типа _Bool (‹stdbool.h›). В противном случае бит = !! (число & (1 ‹< x)); буду.. - person Chris Young; 16.11.2008
comment
Подобные решения работают только в том случае, если целевая переменная имеет целочисленный тип. Более общие решения могут быть приняты для работы с другими полезными целевыми типами, такими как массивы. - person John Zwinck; 04.01.2009
comment
Это только я, но я бы предпочел заключить в скобки выражения битового сдвига (аналогично тому, что сделал @Aaron выше). - person Happy Green Kid Naps; 09.05.2013
comment
Кстати, бит-игра здесь будет молча терпеть неудачу, если number шире, чем int - person anatolyg; 09.05.2013
comment
почему бы тебе не изменить последний на bit = (number >> x) & 1 - person aaronman; 26.06.2013
comment
@Aaron: x86 имеет собственный битовый тест, а также инструкции по установке / очистке / дополнению - person phuclv; 01.08.2013
comment
Думаю, проблема в обозначениях. Скажем, у меня есть 0011 (десятичное 3), и я хочу проверить, установлен ли второй бит, например, тот, который выделен жирным шрифтом: 0 0 1 1. Как вы относитесь к Икс? Будет ли это 2-й или 1-й бит в соответствии с вашей записью, потому что, если это 2-й бит, я думаю, ваше предложение не сработает, например, поскольку вы сдвинете 1 два раза и получите 100 - что не даст 2-й бит как Я определил выше. Не так ли? - person ; 14.09.2013
comment
1 - это int литерал, который подписан. Таким образом, все операции здесь работают с числами со знаком, что не очень хорошо определено стандартами. Стандарты не гарантируют дополнение до двух или арифметический сдвиг, поэтому лучше использовать 1U. - person Siyuan Ren; 10.12.2013
comment
Чтобы прояснить комментарий анатолика, константа 1 без модификаторов определяется как int со знаком. Чтобы это работало правильно для всех переменных, используйте вместо этого 1ULL. - person JonS; 08.07.2014
comment
@JonS для всех переменных размером до unsigned long long в любом случае ... могут быть расширения, определяемые реализацией, такие как __int128. Чтобы быть сверхбезопасным (uintmax_t)1 << x - person M.M; 07.02.2015
comment
Я предпочитаю number = number & ~(1 << n) | (x << n); для изменения n-го бита на x. - person jiasli; 24.03.2015
comment
@Jiashuo Li: этот оператор завершится ошибкой, если number больше int и n больше или равно количеству битов в int. В этом случае он даже вызывает неопределенное поведение. number = number & ~((uintmax_t)1 << n) | ((uintmax_t)x << n); - это общее выражение, которое должно работать для всех размеров number, но может генерировать уродливый и неэффективный код для меньших размеров. - person chqrlie; 24.03.2015
comment
Относится к проверке немного: почему бы просто не использовать число & 0x01 для проверки первого бита, число & 0x08 для четвертого и т. Д. Imho более красивое. - person Anonymous; 08.04.2015
comment
Использование таких операций, как -x, небезопасно, поскольку стандарт C позволяет целым числам со знаком быть (например) величиной знака, дополнением единиц или любой другой системой, которая может выражать необходимый диапазон, что означает, что -x не должно быть таким же, как (~x) + 1. Это не так уж важно для современных архитектур, но вы никогда не знаете, что достаточно умный оптимизирующий компилятор сделает с вашим кодом. - person Kevin; 18.01.2016
comment
Почему мы используем ~ вместо !? - person 71GA; 30.01.2017
comment
@ Анонимный. Мы все равно используем маски, так что если нам когда-нибудь понадобится сделать более одного расширения, очевидно? Примечание: верхний / нижний регистр в EBCDIC составляет 1 бит. - person mckenzm; 09.03.2017
comment
Разве последний пример не предполагает представление с дополнением до двух? Разве это не плохо / непереносимо, как бы вы это ни называли? - person Patrick Roberts; 15.06.2017
comment
@ 71GA: Побитовое отрицание (~) инвертирует все биты, поэтому ~0xFFF0FFFF равно 0x000F0000. Логическое не (!) дает 0, если значение не равно нулю, или 1, если значение равно нулю, поэтому !0xFFF0FFFF равно 0x00000000. - person Adrian McCarthy; 05.09.2017
comment
@PatrickRoberts: Да, это требует дополнения до двух. В дополнении -1 равно 0b1111..1110. (Все единицы - это -0). C ++ также допускает целочисленные представления знака / величины, где -x просто переворачивает бит знака. Я обновил этот ответ, чтобы указать на это. Совершенно нормально, если вы ориентируетесь только на две дополнительные реализации C ++. Это не UB, это просто определяется реализацией, поэтому для правильной работы требуется определить целые числа со знаком как дополнение до 2. Я также изменил константы на 1UL, отметив, что может потребоваться 1ULL. - person Peter Cordes; 10.11.2017
comment
@Kevin: Это гарантированно безопасно для реализации C ++, в которой используется дополнение 2. Это реализация, а не UB. Следует беспокоиться о переносимости до дополнения или знака / величины, а не об оптимизации компиляторов. - person Peter Cordes; 10.11.2017
comment
@Peter -x - это UB, если x - INT_MIN. - person Kevin; 10.11.2017
comment
@Kevin: да ладно, должно быть -(unsigned long)x, что также обходило бы представление целых чисел со знаком. (беззнаковое основание 2 соответствует семантике дополнения до 2). Но оно работает правильно, только если x равно 0 или 1. Помните, что мы устанавливаем бит n на x. - person Peter Cordes; 11.11.2017
comment
@PeterCordes: Это вполне разумно. Но когда разработчик сообщает мне, что этот инвариант будет всегда сохраняться! Я очень нервничаю. - person Kevin; 11.11.2017
comment
@Kevin: Я решил опустить любое упоминание UB с подписанным переполнением в моем последнем редактировании, потому что вы все равно хотите использовать unsigned для портативных 0U - 1U - ›all-one. Как это выглядит сейчас? Я постарался, чтобы ответ был простым. Я уже упоминал, что вам может потребоваться логическое преобразование с помощью !!x. Если бы это был мой собственный ответ, я мог бы вставить больше текста о том, чтобы всегда использовать беззнаковый, но я просто сохраняю этот старый канонический ответ. (Джереми, надеюсь, вам понравятся изменения, возможно, вы захотите отредактировать, чтобы изложить мои изменения своими словами или что-то еще, что вы хотите сказать почти 9 лет спустя.) - person Peter Cordes; 11.11.2017
comment
В истории редактирования только что заметил, что изменение n-го бита в раздел x было добавлено другим сторонним редактированием и не было работой Джереми. - person Peter Cordes; 11.11.2017
comment
Является ли ваш метод изменения n-го бита на x действительным и для C90? И справедливо ли это для чисел без знака? - person avivgood2; 22.04.2020
comment
Я сталкивался с этим довольно редко и всегда думал, что мне нужно придумать одно выражение для выполнения операции set bit to value. Обычно я делаю save state + unconditionally set/clear + restore state аппаратно. Во всяком случае, я случайно наткнулся на это и обязательно украду выражение выше. - person sherrellbc; 15.01.2021
comment
number = (number | (1UL << n)) ^ (!x << n) Упрощено удаление логического и побитового не - person Shogan Aversa-Druesne; 18.01.2021
comment
Вышеупомянутое выражение ^^^^^ позволяет x быть неограниченным, x может быть 0 или любым положительным числом для истины и не привязан к 0 и 1 - person Shogan Aversa-Druesne; 18.01.2021
comment
Иногда мне интересно, не было бы более эффективным использование простого оператора if / else при изменении n -го бита на x. Не number = (number & ~(1UL << n)) | (x << n); генерирует два битовых сдвига, один переворот байта, одно И, одно ИЛИ и, наконец, одно присваивание. В то время как if (x) { number |= (1 << n); } else { number &= ~(1 << n); } генерирует одно сравнение, сдвиг на один бит, либо ИЛИ , или И с обращением байта, а также присваивание. 6 против 5 операций в худшем случае (сброс бита). 6 против 4 при установке бита. Намного более читабельным. Но, может быть, некоторые операции обходятся дороже? - person Smartskaft2; 02.05.2021
comment
@ Smartskaft2 проблема в том, что ветвление с помощью if / else может быть намного дороже. Найдите ошибку предсказания ветвления или посмотрите этот вопрос: stackoverflow.com/q/11227809 - person Angra Mainyu; 03.05.2021
comment
Ошибки предсказания ветки @AngraMainyu пренебрежимо малы: godbolt.org/z/vascq9Khc если решение равно 1,2 раз быстрее. - person JulianH; 26.05.2021
comment
@JulianH Спасибо за тестирование. Но в этом тесте у нас есть фиксированный шаблон чередования истинно / ложно в условии перехода, который предсказатель перехода, вероятно, точно предскажет после первых нескольких итераций. Таким образом, тест на самом деле не измеряет ошибку предсказания ветвления. - person Angra Mainyu; 27.05.2021
comment
@AngraMainyu ЦП не предсказывает идеально, как вы сказали, поскольку существует разница в ускорении между чередующимся и не чередующимся x, однако при рандомизации значения x не-if-решение примерно вдвое быстрее, чем if-решение: godbolt.org/z/5aEbKchzf Итак, да, вы правы, предсказание ветвления убивает производительность. - person JulianH; 28.05.2021

Использование стандартной библиотеки C ++: std::bitset<N>.

Или версия Boost: _ 2_.

Свое катать не нужно:

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

person Martin York    schedule 18.09.2008
comment
+1. Не то, чтобы std :: bitset можно было использовать из C, но поскольку автор пометил свой вопрос с помощью C ++, AFAIK, ваш ответ здесь лучший ... std :: vector ‹bool› - другой способ, если кто-то знает его плюсы и минусы - person paercebal; 19.09.2008
comment
Отлично, Мартин! Вы даже можете использовать перечисление для «индексации» битов: enum {cEngineOn, cDoorsOpen, cAircoOn}; std :: bitset ‹cNBBITS› mybits; mybits [cEngineOn] .set (); const bool cbDoorOpen = mybits [cDoorsOpen]; ... - person xtofl; 27.09.2008
comment
среди ответов здесь я думаю, что это лучший способ управлять битами ... но я думаю, что суть вопроса заключалась в том, как манипулировать битами вручную. еще, + голосование :) - person moogs; 15.10.2008
comment
@paercebal: vector ‹bool› не очень эффективен, так как bool в C ++ занимает полный байт пространства вместо 1 бита… - person andrewdotnich; 13.11.2008
comment
@andrewdotnich: vector ‹bool› - это (к сожалению) специализация, которая хранит значения в виде битов. См. gotw.ca/publications/mill09.htm для получения дополнительной информации ... - person Niklas; 12.12.2008
comment
Возможно, никто не упомянул об этом, потому что это было помечено как встроенное. В большинстве встроенных систем вы избегаете STL как чумы. И поддержка ускорения, вероятно, очень редкая птица, которую можно обнаружить среди большинства встраиваемых компиляторов. - person Lundin; 18.08.2011
comment
@Lundin: Первая точка неверна (некоторые вещи в STL избегаются, но такой общий оператор является общим, std :: bitset прекрасен и ничего не стоит использовать). Пункт второй Boost :: dynamic_bitset ни от чего не зависит и может быть легко использован. - person Martin York; 18.08.2011
comment
@Martin Это правда. Помимо конкретных убийц производительности, таких как STL и шаблоны, многие встроенные системы даже полностью избегают использования стандартных библиотек, потому что их очень сложно проверить. Большая часть встраиваемой ветви охватывает такие стандарты, как MISRA, для которых требуются инструменты статического анализа кода (кстати, такие инструменты должны использовать любые профессионалы в области программного обеспечения, а не только встроенные специалисты). Обычно у людей есть дела поважнее, чем запускать статический анализ через всю стандартную библиотеку - если ее исходный код даже доступен для них в конкретном компиляторе. - person Lundin; 19.08.2011
comment
@Lundin: Ваши утверждения слишком широки (поэтому спорить бесполезно). Я уверен, что могу найти ситуации, в которых они правдивы. Это не меняет моей исходной точки. Оба эти класса идеально подходят для использования во встроенных системах (и я точно знаю, что они используются). Ваша первоначальная точка зрения о том, что STL / Boost не используется во встроенных системах, также неверна. Я уверен, что есть системы, которые их не используют, и даже системы, которые их используют, они используются разумно, но говорить, что они не используются, просто неверно (потому что есть системы, в которых они используются). - person Martin York; 19.08.2011
comment
@ jons34yp: Документация SGI более коническая и обычно содержит меньше ошибок, чем cppreference. - person Martin York; 24.04.2013
comment
В теме четко указан язык C, не C ++. Ясно, что это неправильный ответ, так как же он получил 120+ голосов ?! - person mloskot; 10.09.2013
comment
@mloskot: вопрос четко указывает на C ++. Его в теге под вопросом, какие языки допустимы. Он получил более 120 голосов, потому что люди, которые умеют читать, читают вопрос и правильно оценивают его в контексте тегов. :-) Ох. И это просто. - person Martin York; 11.09.2013
comment
@LokiAstari Осмелюсь заявить, что либо тег неправильный, либо тема и тела неточны. Это важный вопрос, поскольку он приводит к тому, что ТАК вопросы становятся расплывчатыми, а значит, на самом деле бесполезными. - person mloskot; 11.09.2013
comment
Все, что я знаю, это то, что каждый раз, когда я пытаюсь скомпилировать что-то, сделанное на C ++, Boost становится моим новым злейшим врагом. Все равно проголосовали за. - person sudo; 13.08.2014
comment
@ 9000: Почему? Вы не должны ничего делать с наддувом. Это библиотека только для заголовков, и установка Boost должна быть связана с управлением пакетами sudo apt-get install boost-devel - person Martin York; 13.08.2014
comment
Вопрос задан для C / C ++, поэтому, чтобы быть совместимым с обоими мирами, я думаю, что STL здесь неприменим. - person Luis Colorado; 30.09.2014
comment
@LuisColorado: Но если не говорить об этом, значит, информация будет недоступна для пользователей. Дело не в том, чтобы ограничить ваш ответ, а в том, чтобы предложить решение. ОП решит, актуально ли это, поставив галочку рядом с лучшим ответом. Вы голосуете «за», если считаете, что это полезный для сообщества ответ, или «против», если он не имеет ценности для сообщества. - person Martin York; 30.09.2014
comment
@LuisColorado: Я не согласен. Это решило проблему способом, о котором не упоминали другие. По общему признанию, это было для части людей, которые использовали C ++, но вопрос был помечен как C ++ и, следовательно, правильный ответ. - person Martin York; 02.10.2014
comment
@LokiAstari, и по этой причине я только что оставил комментарий, без отрицательного голоса, ничего больше ... просто комментарий. - person Luis Colorado; 05.10.2014
comment
@MartinYork Мир встроенных систем огромен; но если избегать STL и вместо этого некоторые контейнеры и т. д. написаны самостоятельно, код почти наверняка будет менее производительным и будет содержать больше ошибок. (Я работал над одной такой системой в начале 2000-х. Она была хорошо встроена, информационно-развлекательная, но 5 лет назад у нее были ресурсы ПК.) - person Peter - Reinstate Monica; 22.01.2020
comment
@ Peter-ReinstateMonica, мы прямо говорим о std::bitset только здесь. - person Martin York; 22.01.2020
comment
@MartinYork Я пытался поддержать ваше заявление; STL / Boost, который не используется во встроенных системах, также неверен, это правильно. Это используется. (А иногда, когда система достаточно функциональна, а программное обеспечение достаточно сложное, на самом деле является ошибкой не использовать STL, потому что альтернативы - без дженериков или самодельные - хуже. ) - person Peter - Reinstate Monica; 22.01.2020

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

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

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

person Ferruccio    schedule 11.09.2008
comment
Я всегда считал, что использование битовых полей - плохая идея. У вас нет контроля над порядком, в котором распределяются биты (сверху или снизу), что делает невозможным сериализацию значения стабильным / переносимым способом, кроме побитового. Также невозможно смешивать битовую арифметику DIY с битовыми полями, например, создать маску, которая проверяет сразу несколько битов. Конечно, вы можете использовать && и надеяться, что компилятор правильно его оптимизирует ... - person R.. GitHub STOP HELPING ICE; 28.06.2010
comment
Битовые поля настолько плохи во многих отношениях, что я мог бы написать об этом книгу. Фактически, мне почти пришлось сделать это для небольшой полевой программы, которая требовала соответствия MISRA-C. MISRA-C предписывает документировать все поведение, определяемое реализацией, поэтому в итоге я написал эссе обо всем, что может пойти не так в битовых полях. Порядок битов, порядок байтов, биты заполнения, байты заполнения, различные другие проблемы с выравниванием, неявные и явные преобразования типов в битовое поле и из него, UB, если int не используется, и так далее. Вместо этого используйте побитовые операторы для уменьшения количества ошибок и переносимого кода. Битовые поля полностью избыточны. - person Lundin; 18.08.2011
comment
Как и большинство языковых функций, битовые поля можно использовать правильно или злоупотреблять ими. Если вам нужно упаковать несколько небольших значений в один int, битовые поля могут быть очень полезны. С другой стороны, если вы начнете делать предположения о том, как битовые поля сопоставляются с фактическим содержащим int, вы просто напрашиваетесь на проблемы. - person Ferruccio; 18.08.2011
comment
Значит, порядок битов произвольный, и вы не можете использовать его для вращения отдельных битов в микроконтроллере? - person endolith; 08.03.2012
comment
@endolith: Это было бы плохой идеей. Вы можете заставить его работать, но это не обязательно будет переносимым на другой процессор, или на другой компилятор, или даже на следующую версию того же компилятора. - person Ferruccio; 09.03.2012
comment
Это круто. Однако мой вопрос: после проверки sizeof(mybits) я получаю 12 (т.е. размер трех ints). Это место, выделенное в памяти, или какая-то ошибка в функции sizeof? - person iGbanam; 29.11.2012
comment
@Yasky - какой компилятор вы используете? Я получаю 4 с VC ++ 11 и Clang 3.1. - person Ferruccio; 29.11.2012
comment
@Ferruccio Я использую gcc версию 4.4.5 - person iGbanam; 29.11.2012
comment
@Р. Можно использовать оба, struct можно поместить внутрь (обычно анонимного) union с целым числом и т. Д. Это работает. (Я понимаю, что это старый тред, кстати) - person Shade; 17.05.2014
comment
@Shade Нет никаких гарантий, что биты битового поля отображаются разумным и предсказуемым образом с другими типами данных в том же объединении. Весь такой код в лучшем случае будет полностью непереносимым. - person Lundin; 14.12.2015
comment
@Yasky и Ferruccio, получающие разные ответы на sizeof () для этого подхода, должны проиллюстрировать проблемы с совместимостью не только между компиляторами, но и между оборудованием. Иногда мы обманываем себя тем, что решили эти проблемы с помощью языков или определенных сред выполнения, но на самом деле все сводится к тому, «будет ли это работать на моей машине?». Вы, внедренные ребята, заслужите мое уважение (и сочувствие). - person Kelly S. French; 08.12.2016
comment
вы можете захотеть __attribute__((packed)) - person ; 28.06.2020

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

/* 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))
person Community    schedule 04.11.2008
comment
Я понимаю, что это сообщение пятилетней давности, но ни в одном из этих макросов нет дублирования аргументов, Дэн - person Robert Kelly; 02.10.2013
comment
BITMASK_CHECK(x,y) ((x) & (y)) должен быть ((x) & (y)) == (y), иначе он вернет неверный результат по многобитной маске (например, 5 vs. 3) / * Привет всем могильщикам :) * / - person brigadir; 11.12.2014
comment
1 должен быть (uintmax_t)1 или аналогичным на тот случай, если кто-то попытается использовать эти макросы в long или более крупном типе - person M.M; 07.02.2015
comment
Или 1ULL работает так же хорошо, как (uintmax_t) в большинстве реализаций. - person Peter Cordes; 11.11.2017
comment
@brigadir: зависит от того, хотите ли вы проверить наличие каких-либо установленных битов или всех установленных битов. Я обновил ответ, включив оба с описательными именами. - person Peter Cordes; 11.11.2017
comment
BITMASK_CHECK_ALL(x,y) может быть реализовано как !~((~(y))|(x)) - person Handy999; 20.11.2018
comment
@ Handy999 Немного легче понять, почему это работает, после применения закона Де Моргана и перестройки, чтобы получить !(~(x) & (y)) - person Tavian Barnes; 13.08.2019
comment
Это было настолько полезно, что я удивляюсь, почему я не пробовал это раньше, особенно #define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b)))) // '!!' to make sure this returns 0 or 1 - person William Martens; 16.02.2021

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

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

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

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

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

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

person dmckee --- ex-moderator kitten    schedule 08.09.2008
comment
В качестве альтернативы вы можете создать функцию clearbits() вместо &= ~. Почему вы используете для этого перечисление? Я думал, что это были для создания группы уникальных переменных со скрытым произвольным значением, но вы присваиваете определенное значение каждой из них. Так в чем же преимущество по сравнению с простым определением их как переменных? - person endolith; 20.12.2011
comment
@endolith: использование enums для наборов связанных констант имеет долгую историю в программировании на языке C. Я подозреваю, что у современных компиляторов единственное преимущество перед const short или чем-то еще заключается в том, что они явно сгруппированы вместе. И когда вы хотите, чтобы они были отличными от битовых масок, вы получаете автоматическую нумерацию. В С ++, конечно, они также образуют отдельные типы, что дает вам небольшую дополнительную статическую проверку ошибок. - person dmckee --- ex-moderator kitten; 22.12.2011
comment
Вы попадете в неопределенные константы перечисления, если не определите константу для каждого из возможных значений битов. Например, какое значение enum ThingFlags для ThingError|ThingFlag1? - person Luis Colorado; 30.09.2014
comment
Если вы используете этот метод, имейте в виду, что константы перечисления всегда имеют знаковый тип int. Это может вызвать всевозможные тонкие ошибки из-за неявного целочисленного продвижения или побитовых операций над знаковыми типами. thingstate = ThingFlag1 >> 1, например, вызовет поведение, определяемое реализацией. thingstate = (ThingFlag1 >> x) << y может вызывать неопределенное поведение. И так далее. На всякий случай всегда приводите к беззнаковому типу. - person Lundin; 14.12.2015
comment
@Lundin: Начиная с C ++ 11, вы можете установить базовый тип перечисления, например: enum My16Bits: unsigned short { ... }; - person Aiken Drum; 15.03.2016

Из 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 в поле единиц. Это достигается с помощью дополнения к 1 того же выражения, которое обозначено оператором тильда (~).

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

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

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

---- [TEST.C] ----------------------------------------- -----------------------

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

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

int bitmanip(int word)
{
      word = BitSet(word, 2);
      word = BitSet(word, 7);
      word = BitClr(word, 3);
      word = BitFlp(word, 9);
      return word;
}

---- [TEST.OUT (в разобранном виде)] -------------------------------------- ---------

Module: C:\BINK\tst.c
Group: 'DGROUP' CONST,CONST2,_DATA,_BSS

Segment: _TEXT  BYTE   00000008 bytes  
 0000  0c 84             bitmanip_       or      al,84H    ; set bits 2 and 7
 0002  80 f4 02                          xor     ah,02H    ; flip bit 9 of EAX (bit 1 of AH)
 0005  24 f7                             and     al,0f7H
 0007  c3                                ret     

No disassembly errors

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

person yogeesh    schedule 17.09.2008
comment
2 вещи об этом: (1) при просмотре ваших макросов некоторые могут ошибочно полагать, что макросы на самом деле устанавливают / очищают / переворачивают биты в arg, однако присваивания нет; (2) ваш test.c не завершен; Я подозреваю, что если вы запустите больше случаев, вы обнаружите проблему (упражнение для читателя) - person Dan; 18.10.2008
comment
-1 Это просто странная обфускация. Никогда не изобретайте заново язык C, скрывая синтаксис языка за макросами, это очень плохая практика. Затем некоторые странности: сначала 1L подписан, то есть все битовые операции будут выполняться над подписанным типом. Все, что передано этим макросам, будет возвращено как подписанное. Нехорошо. Во-вторых, это будет работать очень неэффективно на меньших ЦП, так как будет выполняться долго, когда операции могли быть на уровне int. В-третьих, макросы, подобные функциям, являются корнем всех зол: у вас вообще нет типобезопасности. Кроме того, очень верен предыдущий комментарий об отсутствии назначения. - person Lundin; 18.08.2011
comment
Это не удастся, если arg равно long long. 1L должен быть максимально широким типом, поэтому (uintmax_t)1. (Вам может сойти с рук 1ull) - person M.M; 07.02.2015
comment
Вы оптимизировали размер кода? На основных процессорах Intel вы получите остановку частичного регистра при чтении AX или EAX после возврата этой функции, потому что она записывает 8-битные компоненты EAX. (Это нормально для процессоров AMD или других, которые не переименовывают частичные регистры отдельно от полного регистра. Haswell / Skylake не переименовывают AL отдельно, но они переименовывают AH.). - person Peter Cordes; 11.11.2017

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

Пример:

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)
person kapilddit    schedule 05.06.2012

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

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

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

person John U    schedule 14.06.2012
comment
Micro - это Coldfire MCF52259, использующий C в Codewarrior. Рассмотрение дизассемблера / asm - полезное упражнение, поскольку оно показывает все шаги, которые должен пройти ЦП, чтобы выполнить даже самую простую операцию. ‹Br› Мы также заметили другие инструкции по загрузке ЦП в критических по времени циклах - ограничение переменной, выполняя var% = max_val, каждый раз требует кучи циклов ЦП, тогда как выполнение if (var ›max_val) var- = max_val использует только пара инструкций. ‹Br› Хорошее руководство по еще нескольким уловкам: codeproject.com/Articles/6154/ - person John U; 19.06.2012
comment
Что еще более важно, вспомогательные регистры ввода-вывода с отображением в память обеспечивают механизм атомарных обновлений. Чтение / изменение / запись могут произойти очень плохо, если последовательность прервана. - person Ben Voigt; 22.02.2015
comment
Имейте в виду, что все регистры портов будут определены как volatile, и поэтому компилятор не может выполнять какие-либо оптимизации кода, включающего такие регистры. Поэтому рекомендуется разобрать такой код и посмотреть, как он получится на уровне ассемблера. - person Lundin; 14.12.2015

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

и т.п.

person R.. GitHub STOP HELPING ICE    schedule 13.07.2010
comment
Это хорошо читать, но следует знать о возможных побочных эффектах. Использование BITOP(array, bit++, |=); в цикле, скорее всего, не приведет к тому, что хочет вызывающий. - person foraidt; 13.07.2010
comment
Действительно. =) Один из вариантов, который вы могли бы предпочесть, - разделить его на 2 макроса, один для адресации элемента массива, а другой для смещения бита на место, ala BITCELL(a,b) |= BITMASK(a,b); (оба принимают a в качестве аргумента для определения размера, но последний никогда не будет оцените a, поскольку он появляется только в sizeof). - person R.. GitHub STOP HELPING ICE; 13.07.2010
comment
@R .. Этот ответ действительно старый, но в этом случае я, вероятно, предпочел бы функцию макросу. - person PC Luddite; 23.10.2015
comment
Незначительное: третье (size_t) приведение, кажется, существует только для того, чтобы подстраховаться от беззнаковой математики с %. Мог (unsigned) там. - person chux - Reinstate Monica; 27.09.2017
comment
(size_t)(b)/(8*sizeof *(a)) без необходимости может сузить b перед разделением. Только проблема с очень большими битовыми массивами. Еще интересный макрос. - person chux - Reinstate Monica; 27.09.2017

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

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

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

person Roddy    schedule 06.11.2008
comment
Практически все, что касается битовых полей, определяется реализацией. Даже если вам удастся узнать все подробности о том, как ваш конкретный компилятор реализует их, использование их в вашем коде наверняка сделает его непереносимым. - person Lundin; 18.08.2011
comment
@Lundin - Верно, но бит-игра встроенной системы (особенно в аппаратных регистрах, к чему и относится мой ответ) в любом случае никогда не будет полезной переносимостью. - person Roddy; 20.08.2011
comment
Возможно, не между совершенно разными процессорами. Но вы, скорее всего, захотите, чтобы его можно было переносить между компиляторами и между разными проектами. И есть много встроенных битов, которые вообще не связаны с оборудованием, например, кодирование / декодирование протокола данных. - person Lundin; 20.08.2011
comment
... и если вы привыкнете использовать битовые поля для встроенного программирования, вы обнаружите, что ваш код X86 работает быстрее и компактнее. Не в простых тестах, где у вас есть вся машина, чтобы сокрушить тест, а в реальных многозадачных средах, где программы конкурируют за ресурсы. Преимущество CISC - первоначальная цель которого заключалась в том, чтобы компенсировать более быстрые процессоры, чем шины, и медленную память. - person ; 16.02.2013

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

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

person John Zwinck    schedule 03.01.2009

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

#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)))
person bill    schedule 13.06.2009
comment
CHAR_BIT уже определен limits.h, вам не нужно вставлять свой собственный BITS (и на самом деле вы сделаете свой код хуже, сделав это) - person M.M; 07.02.2015

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

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

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

    return num;
}
person Community    schedule 11.04.2009
comment
Что ж, он использует неэффективное ветвление. - person asdf; 02.07.2011
comment
@asdf Задача компилятора - выводить наиболее эффективный двоичный файл, работа программиста - написать понятный код. - person M.M; 07.02.2015
comment
Это хорошая демонстрация тестирования, установки и очистки определенного бита. Однако это очень плохой подход для небольшого переключения. - person Ben Voigt; 22.02.2015

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

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 и т. Д. Правильно сопоставляются друг с другом в памяти (основная причина того, что процессоры с прямым порядком байтов `` лучше '', чем процессоры с прямым порядком байтов, я чувствую, что приближается война пламени на...).

person Tim Ring    schedule 17.09.2008
comment
Не используйте таблицу для функции, которая может быть реализована с помощью одного оператора. TQuickByteMask [n] эквивалентен (1 ‹---------------- n). Кроме того, очень плохая идея - делать ваши аргументы краткими. На самом деле / ​​и% будут делением, а не побитовым сдвигом / побитовым и, поскольку знаковое деление на степень 2 не может быть реализовано побитовым. Вы должны сделать тип аргумента беззнаковым int! - person R.. GitHub STOP HELPING ICE; 28.06.2010
comment
Какой в ​​этом смысл? Это только делает код медленнее и труднее для чтения? Я не вижу в этом ни единого преимущества. Программистам на C легче читать 1u ‹< n, и мы надеемся, что он может быть переведен в инструкцию ЦП с одним тактовым тиком. С другой стороны, ваше деление будет преобразовано примерно в 10 тиков или даже до 100 тиков, в зависимости от того, насколько плохо конкретная архитектура обрабатывает деление. Что касается функции растрового изображения, было бы разумнее иметь таблицу поиска, переводящую каждый битовый индекс в байтовый индекс для оптимизации скорости. - person Lundin; 18.08.2011
comment
Что касается big / little endian, big endian будет отображать целые числа и необработанные данные (например, строки) таким же образом: слева направо от msb до lsb по всему растровому изображению. В то время как little endian будет отображать целые числа слева направо как 7-0, 15-8, 23-18, 31-24, но сырые данные по-прежнему идут слева направо от msb до lsb. Так что, насколько меньший порядок байтов лучше для вашего конкретного алгоритма, я полностью не понимаю, кажется, наоборот. - person Lundin; 18.08.2011
comment
@R .. Таблица может быть полезна, если ваша платформа не может эффективно сдвигаться, как старые микрочиповые микроконтроллеры, но, конечно, тогда деление в образце абсолютно неэффективно - person jeb; 18.11.2011

Расширяя ответ 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;
}
person kendotwill    schedule 08.05.2014

Давайте сначала предположим несколько вещей
num = 55 Целое число для выполнения побитовых операций (установка, получение, очистка, переключение).
n = 4 0 на основе позиции разряда для выполнения побитовых операций.

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

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

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

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

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

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

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

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

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

num ^= (1 << n);    // Equivalent to; num = num ^ (1 << n);

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

  • Если бит для переключения равен 0, тогда 0 ^ 1 => 1.
  • Если бит для переключения равен 1, то 1 ^ 1 => 0.
       0000 0001 (1 in decimal)
    <<         4 (left shift 4 times)
-----------------
       0001 0000
     ^ 0011 0111 (55 in decimal)
-----------------
    => 0010 0111 (final result)

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

person Pankaj Prakash    schedule 10.06.2019
comment
Спасибо за подробное объяснение. Вот ссылка на практическую задачу для BIT Magic ссылка - person Chandra Shekhar; 28.12.2019

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

person Community    schedule 30.12.2012
comment
std :: bitset действительно реализован в виде бит большинством компиляторов. - person galinette; 17.11.2014
comment
@galinette, согласен. Заголовочный файл #include ‹bitset› является хорошим ресурсом в этом отношении. Также есть специальный класс vector ‹bool›, когда вам нужно изменить размер вектора. C ++ STL, 2-е издание, Николай М. Йозаттис исчерпывающе описывает их на страницах 650 и 281 соответственно. C ++ 11 добавляет несколько новых возможностей в std :: bitset, особый интерес для меня представляет хеш-функция в неупорядоченных контейнерах. Спасибо за внимание! Я собираюсь удалить свой комментарий о спазме мозга. В сети уже достаточно мусора. Я не хочу ничего к этому добавлять. - person ; 18.11.2014
comment
Это использует как минимум целый байт памяти для каждого bool. Возможно, даже 4 байта для настроек C89, которые используют int для реализации bool - person M.M; 07.02.2015
comment
@MattMcNabb, вы правы. В C ++ размер типа int, необходимый для реализации логического значения, не указан стандартом. Некоторое время назад я понял, что этот ответ был ошибочным, но решил оставить его здесь, поскольку люди, по-видимому, находят его полезным. Для тех, кто хочет использовать биты, комментарий Галинетт наиболее полезен, как и моя битовая библиотека здесь ... stackoverflow.com/a/16534995/1899861 < / а> - person ; 12.02.2015
comment
@RocketRoy: Тогда, наверное, стоит изменить предложение, в котором говорится, что это пример битовых операций. - person Ben Voigt; 22.02.2015

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

person Jeegar Patel    schedule 27.05.2016

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

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)
person sam msft    schedule 06.02.2015

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

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

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

person chux - Reinstate Monica    schedule 27.09.2017
comment
Интересный взгляд на старый вопрос! Ни number |= (type_of_number)1 << x;, ни number |= (number*0 + 1) << x; не подходят для установки бит знака подписанного типа ... Фактически, ни то, ни другое не является number |= (1ull << x);. Есть ли портативный способ сделать это по позиции? - person chqrlie; 28.09.2017
comment
@chqrlie IMO, лучший способ избежать установки знакового бита и риска UB или IDB со сдвигами - использовать типы unsigned. Переносимый код сдвига подписанный слишком запутан, чтобы быть приемлемым. - person chux - Reinstate Monica; 28.09.2017

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

Шаблонная версия (помещена в файл заголовка) с поддержкой изменения нескольких бит (работает на микроконтроллерах 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.

person Joakim L. Christiansen    schedule 10.02.2018
comment
Этот код не работает. (Кроме того, почему у вас есть ; после определений функций?) - person melpomene; 10.02.2018
comment
@melpomene Код не битый, тестировал. Вы имеете в виду, что он не будет компилироваться или результат неправильный? О дополнительном ';' Не помню, их действительно можно убрать. - person Joakim L. Christiansen; 25.02.2018
comment
Спасибо, что заметили, это должно было быть ((variable & bits) == bits) - person Joakim L. Christiansen; 27.02.2018
comment
используйте std::bitsetin c ++ 11 - person pqnet; 25.10.2019
comment
К сожалению, вы даже не можете увидеть код без прокрутки в сторону, так как он очень многословен. Возможно, поможет несколько символов новой строки? - person Asteroids With Wings; 22.04.2020

Установка 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.

person Dominic van der Zypen    schedule 06.02.2021
comment
Это могло бы быть намного более кратко (и, вероятно, более эффективно, если компилятор не оптимизирует ваше решение) как (number & ~(1 << n)) | (!!x << n). - person Will Eccles; 08.05.2021

Вот процедура на C для выполнения основных побитовых операций:

#define INT_BIT (unsigned int) (sizeof(unsigned int) * 8U) //number of bits in unsigned int

int main(void)
{
    
    unsigned int k = 5; //k is the bit position; here it is the 5th bit from the LSb (0th bit)
    
    unsigned int regA = 0x00007C7C; //we perform bitwise operations on regA
    
    regA |= (1U << k);    //Set kth bit
    
    regA &= ~(1U << k);   //Clear kth bit
    
    regA ^= (1U << k);    //Toggle kth bit
    
    regA = (regA << k) | regA >> (INT_BIT - k); //Rotate left by k bits
    
    regA = (regA >> k) | regA << (INT_BIT - k); //Rotate right by k bits

    return 0;   
}

person lckid2004    schedule 17.03.2021

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

char bitfield;

// Start at 0th position

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

Or

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

Or

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;
}
person Vincet    schedule 27.05.2014
comment
value << n может вызвать неопределенное поведение - person M.M; 07.02.2015
comment
измените 1 на 0x1 или 1UL, чтобы не говорить о UB @ M.M - person KPCT; 18.04.2021

person    schedule
comment
Тип возврата check_nth_bit может быть bool. - person Xeverous; 05.05.2020
comment
@Xeverous да, это зависит от намерения вызывающего абонента - person Sazzad Hissain Khan; 05.05.2020