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

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

 rajya vardhan02 февр. 2013 г., 22:17
Эта ссылка помогла мне понять, как на самом деле работают эти операции -cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/setBitI.html Здесь вы можете найти более интересные операции -cs.umd.edu/class/sum2003/cmsc311/Notes
 Luchian Grigore04 нояб. 2013 г., 19:57
@glglgl / Джонатон, он имеет достаточно значения для C ++, чтобы пометить его как таковой. Это исторический вопрос с большим количеством трафика, и тег C ++ поможет другим заинтересованным программистам найти его через поиск в Google.
 anon05 янв. 2009 г., 12:13
Вы также можете быть заинтересованы в проверкеБит Твиддлер, Бит Тиддлинг Хаки, а такжеАгрегированные магические алгоритмы.
 ugasoft18 сент. 2008 г., 18:01
прочитай это:graphics.stanford.edu/~seander/bithacks.html и, когда вы освоите это, прочитайте это:realtimecollisiondetection.net/blog/?p=78
 Joey van Hummel11 авг. 2013 г., 23:21
Я могу легко предположить, что это было не для самого вопроса, а для создания очень полезного справочного руководства. Учитывая, что я попал сюда, когда мне нужно было немного информации, в любом случае.

Ответы на вопрос(26)

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

namespace bit {
    template <typename T1, typename T2> inline void set  (T1 &variable, T2 bit) {variable |=  ((T1)1 << bit);}
    template <typename T1, typename T2> inline void clear(T1 &variable, T2 bit) {variable &= ~((T1)1 << bit);}
    template <typename T1, typename T2> inline void flip (T1 &variable, T2 bit) {variable ^=  ((T1)1 << bit);}
    template <typename T1, typename T2> inline bool test (T1 &variable, T2 bit) {return variable & ((T1)1 << bit);}
}

namespace bitmask {
    template <typename T1, typename T2> inline void set  (T1 &variable, T2 bits) {variable |= bits;}
    template <typename T1, typename T2> inline void clear(T1 &variable, T2 bits) {variable &= ~bits;}
    template <typename T1, typename T2> inline void flip (T1 &variable, T2 bits) {variable ^= bits;}
    template <typename T1, typename T2> inline bool test_all(T1 &variable, T2 bits) {return ((variable & bits) == bits);}
    template <typename T1, typename T2> inline bool test_any(T1 &variable, T2 bits) {return variable & bits;}
}
 melpomene25 февр. 2018 г., 19:25
(variable & bits == bits)?
 melpomene10 февр. 2018 г., 21:11
Этот код не работает. (Кроме того, почему у вас есть; после определения твоей функции?)
 Joakim L. Christiansen27 февр. 2018 г., 21:56
Спасибо, что заметили, это должно было быть((variable & bits) == bits)
 Joakim L. Christiansen25 февр. 2018 г., 16:51
@ melpomene Код не сломан, я его проверил. Ты имеешь в виду, что он не скомпилируется или результат неверный? О лишних ';' Я не помню, они действительно могут быть удалены.

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

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

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

    return num;
}
 M.M07 февр. 2015 г., 00:53
@asdf Задача компилятора - вывести наиболее эффективный двоичный файл, задача программиста - написать чистый код
 Ben Voigt22 февр. 2015 г., 03:18
Это хорошая демонстрация тестирования, установки и очистки определенного бита. Однако это очень плохой подход к переключению немного.
 asdf02 июл. 2011 г., 21:33
Ну, это использует неэффективное ветвление.
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);
}

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

и т.п.

 chux27 сент. 2017 г., 19:58
Незначительный: 3-й(size_t) актерский состав, кажется, существует только для того, чтобыматематика без знака с участием%, Мог(unsigned) там.
 R..13 июл. 2010 г., 11:19
На самом деле. =) Один вариант, который вы могли бы предпочесть, - разделить его на 2 макроса, 1 для адресации элемента массива, а другой для сдвига бита на место, аляBITCELL(a,b) |= BITMASK(a,b); (оба принимаютa в качестве аргумента для определения размера, но последний никогда не будет оцениватьa так как он появляется только вsizeof).
 chux27 сент. 2017 г., 20:00
(size_t)(b)/(8*sizeof *(a)) излишне может сужатьсяb до разделения. Только проблема с очень большими битовыми массивами. Еще интересный макрос.
 foraidt13 июл. 2010 г., 10:27
Это хорошо читать, но нужно знать о возможных побочных эффектах. С помощьюBITOP(array, bit++, |=); в цикле, скорее всего, не будет делать то, что хочет вызывающий.
 PC Luddite23 окт. 2015 г., 19:08
@R .. Этот ответ очень старый, но в этом случае я бы предпочел функцию макросу.

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

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

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

 Ferruccio18 авг. 2011 г., 21:35
Как и большинство языковых функций, битовые поля могут использоваться правильно или ими можно злоупотреблять. Если вам нужно упаковать несколько небольших значений в одно целое, битовые поля могут быть очень полезны. С другой стороны, если вы начинаете делать предположения о том, как битовые поля отображаются на фактическое, содержащее int, вы просто напрашиваетесь на неприятности.
 Kelly S. French08 дек. 2016 г., 17:11
@Yasky и Ferruccio, получающие разные ответы на sizeof () для этого подхода, должны проиллюстрировать проблемы совместимости не только между компиляторами, но и с оборудованием. Мы иногда обманываем себя тем, что решили эти проблемы с помощью языков или определенных сред выполнения, но на самом деле все сводится к тому, «будет ли это работать на моей машине?». Вы, ребята, встраиваемые, уважайте меня (и симпатии).
 Lundin18 авг. 2011 г., 21:19
Битовые поля плохи во многих отношениях, я мог бы почти написать книгу об этом. Фактически, мне почти пришлось сделать это для небольшой программы, которая требовала соответствия требованиям MISRA-C. MISRA-C принудительно документирует все поведение, определяемое реализацией, поэтому я написал довольно эссе обо всем, что может пойти не так в битовых полях. Порядок битов, порядковый номер, биты заполнения, байты заполнения, различные другие проблемы выравнивания, неявные и явные преобразования типов в и из битового поля, UB, если int не используется, и так далее. Вместо этого используйте побитовые операторы для уменьшения количества ошибок и переносимого кода. Битовые поля полностью избыточны.
 R..28 июн. 2010 г., 08:17
Я всегда считал использование битовых полей плохой идеей. У вас нет контроля над порядком, в котором распределяются биты (сверху или снизу), что делает невозможным сериализацию значения стабильным / переносным способом, кроме как по битам. Также невозможно смешать битовую арифметику DIY с битовыми полями, например, создать маску, которая проверяет несколько битов одновременно. Конечно, вы можете использовать && и надеяться, что компилятор правильно его оптимизирует ...
 Ferruccio08 мар. 2012 г., 22:02
@endolith: Это не будет хорошей идеей. Вы можете заставить его работать, но он не обязательно будет переносимым на другой процессор, или на другой компилятор, или даже на следующую версию того же компилятора.

Пример:

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)

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

bool    IsGph[256], IsNotGph[256];

//  Initialize boolean array to detect printable characters
for(i=0; i<sizeof(IsGph); i++)  {
    IsGph[i] = isgraph((unsigned char)i);
}

Итак, к вашему вопросу, IsGph [i] = 1 или IsGph [i] = 0 упрощают настройку и очистку bools.

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

//  Initialize boolean array to detect UN-printable characters, 
//  then call function to toggle required bits true, while initializing a 2nd
//  boolean array as the complement of the 1st.
for(i=0; i<sizeof(IsGph); i++)  {
    if(IsGph[i])    {
         IsNotGph[i] = 0;
    }   else   {
         IsNotGph[i] = 1;
    }
}

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

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

 Ben Voigt22 февр. 2015 г., 03:20
@RocketRoy: Возможно, стоит изменить предложение, которое утверждает, что это пример «битовых операций».
 galinette17 нояб. 2014 г., 21:03
std :: bitset действительно реализован в виде битов большинством компиляторов
 user189986112 февр. 2015 г., 08:23
@ MattMcNabb, вы правы. В C ++ размер типа int, необходимый для реализации логического значения, не указан стандартом. Я понял, что этот ответ был ошибочным некоторое время назад, но решил оставить его здесь, так как люди, по-видимому, находят его полезным. Для тех, кто хочет использовать биты, наиболее полезен комментарий Галинетт, как и моя библиотека битов здесь ...stackoverflow.com/a/16534995/1899861
 user189986117 нояб. 2014 г., 22:08
@galinette, Согласен. Заголовочный файл #include <bitset> является хорошим ресурсом в этом отношении. Кроме того, специальный класс vector <bool> для случаев, когда вам нужно изменить размер вектора. C ++ STL, 2nd Edition, Nicolai Josuttis подробно описывает их на страницах 650 и 281 соответственно. C ++ 11 добавляет несколько новых возможностей в std :: bitset, особый интерес для меня представляет хеш-функция в неупорядоченных контейнерах. Спасибо за головы! Я собираюсь удалить свой комментарий о судорогах. Уже достаточно мусора в сети. Я не хочу добавлять к этому.
 M.M07 февр. 2015 г., 00:55
Это использует по крайней мере целый байт памяти для каждогоbool, Может быть, даже 4 байта для установок C89, которые используютint реализоватьbool

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

Ускоренная версия допускает размерность набора битов по сравнению сстандартная библиотека размер набора битов во время компиляции.

 Martin York19 авг. 2011 г., 08:41
@Lundin: Ваши высказывания слишком широки (поэтому спорить бесполезно). Я уверен, что смогу найти ситуации, если они правдивы. Это не меняет мою начальную точку. Оба эти класса прекрасно подходят для использования во встроенных системах (и я точно знаю, что они используются). Ваше первоначальное мнение о том, что STL / Boost не используется во встроенных системах, также неверно. Я уверен, что есть системы, которые их не используют, и даже те системы, которые их используют, они используются разумно, но говорить, что они не используются, просто неправильно (потому что есть системы, в которых они используются).
 paercebal19 сент. 2008 г., 20:16
+1. Не то, чтобы std :: bitset можно было использовать из "C", но поскольку автор отметил свой вопрос как "C ++", AFAIK, ваш ответ здесь самый лучший ... std :: vector <bool> - это другой способ, если знать его плюсы и минусы
 Niklas12 дек. 2008 г., 21:40
@andrewdotnich: vector <bool> - это (к сожалению) специализация, которая хранит значения в виде битов. Видетьgotw.ca/publications/mill09.htm для получения дополнительной информации...
 Lundin18 авг. 2011 г., 21:47
Может быть, никто не упомянул об этом, потому что это было помечено как встроенное. В большинстве встроенных систем вы избегаете STL, как чумы. И поддержка буста, вероятно, очень редкая птица среди большинства встроенных компиляторов.
 Lundin19 авг. 2011 г., 08:26
@ Мартин Это очень верно. Помимо определенных факторов, снижающих производительность, таких как STL и шаблоны, многие встроенные системы даже полностью избегают использования стандартных библиотек, потому что их очень сложно проверить. Большая часть встраиваемой ветви охватывает стандарты, такие как MISRA, для которых требуются инструменты статического анализа кода (кстати, такие инструменты должны использовать любые профессионалы в области программного обеспечения, а не просто встраиваемые люди). Обычно у людей есть дела поважнее, чем проводить статический анализ через всю стандартную библиотеку - если ее исходный код даже доступен для них на конкретном компиляторе.

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

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

/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK_ALL(x,y) (((x) & (y)) == (y))   // warning: evaluates y twice
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))
 Peter Cordes10 нояб. 2017 г., 22:31
@brigadir: зависит от того, хотите ли вы проверить какие-либо установленные биты или все установленные биты. Я обновил ответ, чтобы включить оба с описательными именами.
 Robert Kelly02 окт. 2013 г., 16:53
Э-э, я понимаю, что это 5-летняя запись, но в этих макросах нет дублирования аргументов, Дэн
 M.M07 февр. 2015 г., 00:50
1 должно быть(uintmax_t)1 или аналогичные в случае, если кто-то пытается использовать эти макросы наlong или больший тип
 Peter Cordes10 нояб. 2017 г., 22:27
Или же1ULL работает так же как(uintmax_t) на большинстве реализаций.
 brigadir11 дек. 2014 г., 13:00
BITMASK_CHECK(x,y) ((x) & (y)) должно быть((x) & (y)) == (y) в противном случае он возвращает неверный результат в многобитной маске (например,5 против3) / * Привет всем могильщикам:) * /

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

struct HwRegister {
    unsigned int errorFlag:1;  // one-bit flag field
    unsigned int Mode:3;       // three-bit mode field
    unsigned int StatusCode:4;  // four-bit status code
};

struct HwRegister CR3342_AReg;

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

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

 Lundin18 авг. 2011 г., 21:50
Практически все, что касается битовых полей, определяется реализацией. Даже если вам удастся выяснить все детали относительно того, как ваш конкретный компилятор реализует их, использование их в вашем коде наверняка сделает его непереносимым.
 Lundin20 авг. 2011 г., 11:35
Возможно, не между совершенно разными процессорами. Но вы, скорее всего, хотите, чтобы он был переносимым между компиляторами и между различными проектами. И есть много встроенных «разборов», которые вообще не связаны с аппаратным обеспечением, таких как кодирование / декодирование протокола данных.
 Roddy19 авг. 2011 г., 22:13
@Lundin - Правда, но битовая встроенная система (особенно в аппаратных регистрах, к которой относится мой ответ) никогда не будет полезной в любом случае.
 user189986115 февр. 2013 г., 23:26
... и если вы привыкнете использовать битовые поля во встроенном программировании, вы обнаружите, что ваш код X86 работает быстрее и эффективнее. Не в простых тестах, где у вас есть целая машина, чтобы преодолеть тест, а в реальных многозадачных средах, где программы конкурируют за ресурсы. Преимущество CISC - первоначальная цель которого заключалась в том, чтобы компенсировать процессоры быстрее, чем шины и медленную память.

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

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

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

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

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

Какие проблемы случаются, когдаnumber это более широкий тип, чем1?
x может быть слишком велик для смены1 << x приводя кнеопределенное поведение (УБ). Даже если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;

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

 chux28 сент. 2017 г., 00:33
@chqrlie IMO, лучший способ избежать установки знакового бита и риска UB или IDB со сдвигами - это использоватьнеподписанный типы. Очень переносимая сменаподписанный код слишком сложен, чтобы быть приемлемым.
 chqrlie28 сент. 2017 г., 00:27
Интересный взгляд на старый вопрос! ниnumber |= (type_of_number)1 << x; ниnumber |= (number*0 + 1) << x; уместно установить знаковый бит типа со знаком ...number |= (1ull << x);, Есть ли портативный способ сделать это по позиции?

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

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

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

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

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

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

 Luis Colorado30 сент. 2014 г., 12:55
Вы попадете в неопределенные константы перечисления, если не определите константу для каждого из возможных значений битов. Что такоеenum ThingFlags значение дляThingError|ThingFlag1, например?
 Aiken Drum15 мар. 2016 г., 16:01
@Lundin: Начиная с C ++ 11, вы можете установить базовый тип перечисления, например:enum My16Bits: unsigned short { ... };
 dmckee22 дек. 2011 г., 02:15
@endolith: использованиеenums для наборов связанных констант уходит далеко в программирование на c. Я подозреваю, что у современных компиляторов единственное преимущество передconst short или что бы то ни было, что они явно сгруппированы вместе. И когда вы хотите их за что-тоДругие чем битовые маски вы получаете автоматическую нумерацию. В C ++, конечно, они также формируют различные типы, что дает вам немного больше статической проверки ошибок.
 Lundin14 дек. 2015 г., 10:25
Если вы используете этот метод, имейте в виду, что константы перечисления всегда имеют тип со знакомint, Это может вызывать любые тонкие ошибки из-за неявного целочисленного продвижения или побитовых операций со знаковыми типами.thingstate = ThingFlag1 >> 1 будет, например, вызывать поведение, определяемое реализацией.thingstate = (ThingFlag1 >> x) << y может вызвать неопределенное поведение. И так далее. Чтобы быть в безопасности, всегда приводите к типу без знака.
 endolith20 дек. 2011 г., 16:09
По-другому вы могли бы сделатьclearbits() функция вместо&= ~, Почему вы используете enum для этого? Я думал, что они предназначены для создания набора уникальных переменных со скрытым произвольным значением, но вы назначаете определенное значение каждой. Так в чем же выгода от определения их как переменных?
Проверьте бит в произвольном месте в переменной произвольного типа:
#define bit_test(x, y)  ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )

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

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

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

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

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

 Lundin14 дек. 2015 г., 10:42
Имейте в виду, что все регистры портов будут определены какvolatile и, следовательно, компилятор не может выполнить какие-либо оптимизации для кода, включающего такие регистры. Поэтому хорошей практикой является дизассемблирование такого кода и просмотр его результатов на уровне ассемблера.
 John U19 июн. 2012 г., 19:33
Микро был Coldfire MCF52259, используя C в Codewarrior. Рассмотрение дизассемблера / ассемблера является полезным упражнением, поскольку оно показывает все шаги, которые ЦП должен пройти, чтобы выполнить даже самые основные операции. <br> Мы также заметили другие инструкции по переключению ЦП в циклы, критичные ко времени - ограничение переменной с помощью var% = max_val стоит кучу циклов ЦП каждый раз, в то время как мы выполняем, если (var> max_val) var- = max_val использует только пара инструкций. <br> Хорошее руководство по нескольким трюкам здесь:codeproject.com/Articles/6154/...
 Ben Voigt22 февр. 2015 г., 03:16
Еще важнее то, что регистры ввода-вывода с отображением вспомогательной памяти обеспечивают механизм атомарных обновлений. Чтение / изменение / запись могут пойти очень плохо, если последовательность прервана.

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

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

int value, pos;

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

value = value | 1 << pos;

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

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

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

value = value ^ 1 << pos;

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

& |

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

foo = foo | 001b

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

if ( foo & 001b ) ....

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

foo = foo & 110b

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

 Lundin14 дек. 2015 г., 10:14
В C. нет двоичных обозначений. Двоичные целочисленные константы являются нестандартным расширением.

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;
}
 M.M07 февр. 2015 г., 00:57
value << n может вызвать неопределенное поведение

#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)))
 M.M07 февр. 2015 г., 00:52
CHAR_BIT уже определеноlimits.hвам не нужно вкладывать свои собственныеBITS (и на самом деле вы делаете свой код хуже, делая это)
Изsnip-c.zipbitops.h:
/*
**  Bit set, clear, and test operations
**
**  public domain snippet by Bob Stout
*/

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

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

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

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

    0000 0000 0000 0000 0000 0000 0000 0001 binary.

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

    0000 0000 0000 0000 0000 0001 0000 0000 binary.

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

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

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

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

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

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

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

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

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

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

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

No disassembly errors

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

 M.M07 февр. 2015 г., 00:51
Это не удастся, еслиarg являетсяlong long. 1L должен быть максимально широкого типа, так(uintmax_t)1 , (Вы можете сойти с1ull)
 Peter Cordes10 нояб. 2017 г., 22:38
Вы оптимизировали для размера кода? На основных процессорах Intel вы получите частичный регистр при чтении AX или EAX после возврата этой функции, поскольку она записывает 8-битные компоненты EAX. (Это нормально для процессоров AMD или других, которые не переименовывают частичные регистры отдельно от полного регистра.Haswell / Skylake не переименовывают AL отдельно, но они переименовывают AH.).
 Dan18 окт. 2008 г., 03:51
2 вещи об этом: (1) при просмотре ваших макросов, некоторые могут ошибочно полагать, что макросы фактически устанавливают / сбрасывают / переворачивают биты в аргументе, однако присваивания нет; (2) ваш test.c не завершен; Я подозреваю, что если вы запустите больше дел, вы найдете проблему (упражнение для чтения)
 Lundin18 авг. 2011 г., 21:14
-1 Это просто странное запутывание. Никогда не изобретайте язык C, скрывая синтаксис языка за макросами, этоочень плохая практика. Затем некоторые странности: сначала подписывается 1L, что означает, что все битовые операции будут выполняться со знаком типа. Все, что передано в эти макросы, вернется как подписано долго. Фигово. Во-вторых, это будет очень неэффективно работать на меньших процессорах, так как оно будет работать долго, когда операции могли выполняться на уровне int. В-третьих, функционально-подобные макросы являются корнем всего зла: у вас нет никакой безопасности типов. Кроме того, предыдущий комментарий об отсутствии назначения очень действителен.

Вот.

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

 Ben Voigt22 февр. 2015 г., 03:20
0x это префикс для литерала в шестнадцатеричном, а не двоичном.

Расширение на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;
}
Решение Вопроса
Установка немного

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

number |= 1UL << n;

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

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

Прояснение немного

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

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

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

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

Оператор 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 или знаком / величиной), использует отрицание без знака.

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.

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

 Chris16 нояб. 2008 г., 08:49
бит = число & (1 << x); не будет помещать значение бита x в бит, если только бит не имеет тип _Bool (<stdbool.h>). В противном случае, бит = !! (число & (1 << x)); будем..
 Siyuan Ren10 дек. 2013 г., 09:53
1 являетсяint литерал, который подписан. Таким образом, все операции здесь выполняются на подписанных номерах, что не очень хорошо определено стандартами. Стандарты не гарантируют двоичного дополнения или арифметического сдвига, поэтому лучше использовать1U.
 Aaron17 сент. 2008 г., 19:13
Я хотел бы отметить, что на платформах, которые имеют встроенную поддержку установки / сброса битов (например, микроконтроллеры AVR), компиляторы часто переводят «myByte | = (1 << x)» в собственные инструкции установки / сброса битов, когда x равен константа, например: (1 << 5) или const без знака x = 5.
 aaronman26 июн. 2013 г., 20:47
почему бы вам не изменить последний наbit = (number >> x) & 1
 Eliko24 мар. 2015 г., 01:38
я предпочитаюnumber = number & ~(1 << n) | (x << n); для изменения n-го бита на x.

Ваш ответ на вопрос