Как вы устанавливаете, очищаете и переключаете один бит? - PullRequest
2337 голосов
/ 07 сентября 2008

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

Ответы [ 26 ]

23 голосов
/ 06 ноября 2008

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

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

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

19 голосов
/ 14 июня 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)))
18 голосов
/ 04 января 2009

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

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

13 голосов
/ 17 сентября 2008

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

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

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

13 голосов
/ 28 февраля 2012

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

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

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

    return num;
}
10 голосов
/ 27 мая 2016

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

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

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

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

10 голосов
/ 08 мая 2014

Расширение на 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;
}
9 голосов
/ 30 декабря 2012

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

bool    IsGph[256], IsNotGph[256];

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

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

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

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

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

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

6 голосов
/ 30 апреля 2012

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

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

...