如何设置,清除和切换单个位?

时间:2020-03-05 18:48:50  来源:igfitidea点击:

如何在C / C ++中设置,清除和切换位?

解决方案

回答

使用按位运算符:&``|

要设置" 000b"的最后一位:

foo = foo | 001b

要检查foo中的最后一位:

if ( foo & 001b ) ....

要清除foo中的最后一位:

foo = foo & 110b

为了清楚起见,我使用了" XXXb"。我们可能将要使用HEX表示形式,具体取决于要打包位的数据结构。

回答

使用按位或者运算符(|)进行设置。

number |= 1UL << n;

那将设置number的第n位。

如果number的宽度大于unsigned long的宽度,则使用1ULL;直到评估" 1UL << n"之后,才发生" 1UL << n"的提升,在这种情况下,未定义的行为是移动超过" long"的宽度。其他所有示例也是如此。

使用按位AND运算符()清除一点。

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

这将清除number的第n位。我们必须使用按位NOT运算符()反转位字符串,然后将其取反。

XOR运算符(^)可用于切换一位。

number ^= 1UL << n;

这将切换数字的第n位。

我们没有要求这样做,但我也可以添加它。

要检查一点,将数字n右移,然后按位与它相加:

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

这会将number的第n位的值放入变量bit中。

在2的补码C ++实现中,可以将第n位设置为1或者0来实现:

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

如果x为1则将设置n位,如果x为0则将其清除。如果x具有其他值,则会产生垃圾。 x = !! x会将其布尔值设为0或者1.

为了使它独立于2的补码取反行为(其中-1设置了所有位,与1的补码或者符号/幅度C ++实现不同),请使用无符号取反。

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

或者

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

使用无符号类型进行可移植位操作通常是一个好主意。

通常也不要复制/粘贴代码,这也是一个好主意,因此许多人使用预处理器宏(例如社区Wiki进一步解答)或者某种封装。

回答

有时值得用一个"枚举"来命名这些位:

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

然后使用这些名称。 IE。写

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

进行设置,清除和测试。这样,我们可以从其余代码中隐藏幻数。

除此之外,我赞同杰里米的解决方案。

回答

另一种选择是使用位字段:

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

这仅适用于固定大小的位字段。否则,我们必须诉诸先前文章中介绍的位旋转技术。

回答

从snip-c.zip的bitops.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()宏中
1字段中的单个0位。这是通过使用1来完成的
由波浪号(〜)运算符表示的相同表达式的补码。

一旦创建了遮罩,便会按照建议将其应用于自变量,
通过使用按位和(&)或者(|)和xor(^)运算符。自面膜
的类型为long,则宏在char,short,int上也能正常工作,
或者长的。

最重要的是,这是对整个类别的通用解决方案
问题。当然,有可能甚至适当地重写
每次我们都可以使用显式掩码值等效于任何这些宏
需要一个,但是为什么呢?请记住,宏替换发生在
预处理程序,因此生成的代码将反映以下事实:
被编译器视为常量,即使用效率相同
通用宏,可以在我们每次需要做的时候"重新发明轮子"
位操作。

不服气?这是我使用Watcom C进行全面优化的一些测试代码
而不使用_cdecl,因此产生的反汇编将与
可能的:

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

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

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

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

---- [TEST.OUT(已分解)] -------------------------------------- ---------

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

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

No disassembly errors

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

回答

如果我们要花很多时间在摆弄,我们可能要使用遮罩,这样可以使整个过程更快。以下功能非常快且仍然很灵活(它们允许在任何大小的位图中旋转位)。

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

请注意,要以16位整数设置位" n",请执行以下操作:

TSetBit( n, &my_int);

我们需要确保位数在我们传递的位图范围内。请注意,对于字节,字,双字,qword等在内存中相互正确映射的小字节序处理器(主要原因是,小字节序处理器比大字节序处理器"更好",是啊,我感到火焰大战即将来临在...)。

回答

使用标准C ++库:std :: bitset <N>。

或者Boost版本:boost :: dynamic_bitset

无需自己动手:

#include <bitset>
#include <iostream>

int main()
{
    std::bitset<5> x;

    x[1] = 1;
    x[2] = 0;
    // Note x[0-4]  valid

    std::cout << x << std::endl;
}
[Alpha:] > ./a.out
00010

与标准库编译时大小的位组相比,Boost版本允许运行时大小的位组。

回答

我使用在头文件中定义的宏来处理位设置和清除:

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

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

回答

位域方法在嵌入式领域具有其他优势。我们可以定义直接映射到特定硬件寄存器中位的结构。

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,但这可能与实现有关。另外,请验证编译器如何处理跨越字节边界的字段。

然后,我们可以像以前一样读取,写入,测试各个值。

回答

在任意类型的变量的任意位置检查一点:

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

笔记:
这被设计为快速(鉴于其灵活性)和非分支。编译Sun Studio 8时,它可以产生有效的SPARC机器代码。我还在amd64上使用MSVC ++ 2008测试了它。可以制作相似的宏来设置和清除位。与许多其他解决方案相比,此解决方案的主要区别在于,它几乎可以在任何类型的变量中的任何位置使用。

回答

用这个:

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

    return num;
}

回答

更一般的,对于任意大小的位图:

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

回答

这是我最喜欢的位算术宏,它适用于从unsigned char到size_t的任何类型的unsigned integer数组(这是应该有效使用的最大类型):

#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, &)) ...

等等。