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

JeffV 2230 c c++ bit-manipulation bitwise-operators

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

Jeremy Ruten.. 3202

设置一下

使用按位OR运算符(|)设置一个位.

number |= 1UL << n;

这将n是最重要的number.n应该为零,如果要设置1st位,依此类推n-1,如果要设置该n位.

使用1ULLif number比宽unsigned long; 1UL << n直到评估1UL << n了未定义的行为偏移超过a的宽度之后才会发生促销long.这同样适用于所有其他示例.

清理一下

使用按位AND运算符(&)清除一位.

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

这将清除一n点点number.必须使用按位NOT运算符(~)反转位串,然后运行AND.

切换了一下

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

number ^= 1UL << n;

这将切换到n第二位number.

检查一下

你没有要求这个,但我不妨补充一下.

要检查一下,将数字n向右移动,然后按位向右移动:

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

这将把n第四位的值number放入变量中bit.

将第n位更改为x

将该n位设置为1或者0可以通过以下2的补码C++实现来实现:

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

n,如果将被设置x1,如果清除x0.如果x有其他价值,你会得到垃圾. x = !!x将它布尔化为0或1.

为了使其独立于2的补码否定行为(其中-1所有位都设置,与1的补码或符号/幅度C++实现不同),使用无符号否定.

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

要么

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

使用无符号类型进行便携式位操作通常是个好主意.

要么

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

(number & ~(1UL << n))将清除该n位并将(x << n)n位置位x.

一般来说,通常不要复制/粘贴代码也是一个好主意,因此许多人使用预处理器宏(如社区维基回答更进一步)或某种封装.

  • 我想要注意的是,在本机支持bit set/clear(例如AVR微控制器)的平台上,只要x为x,编译器就会经常将'myByte | =(1 << x)'转换为本机位设置/清除指令常数,ex:(1 << 5),或const unsigned x = 5. (109认同)
  • bit = number&(1 << x); 除非位具有类型_Bool(<stdbool.h>),否则不会将位x的值放入位.否则,bit = !!(数字&(1 << x)); 将.. (47认同)
  • `1`是一个`int`文字,它是签名的.因此,此处的所有操作都使用有符号数进行操作,而标准并未明确定义.标准不保证二进制补码或算术运算,因此最好使用"1U". (41认同)
  • 我更喜欢`number = number&〜(1 << n)| (x << n);`用于将第n位更改为x. (39认同)
  • 你为什么不把最后一个改成'bit =(number >> x)&1` (21认同)
  • 如果`number`比`int`宽,那么这里的小小的摆弄会默默地失败 (13认同)
  • 为了阐明anatolyg的注释,没有修饰符的常量"1"被定义为有符号的int.要使所有变量都能正常工作,请改用"1ULL". (9认同)
  • 这只是我,但我更喜欢用位移表达式括起来(类似于@Aaron上面所做的). (6认同)
  • @JonS对于所有变量大小都是`unsigned long long`无论如何......可能有实现定义的扩展,例如`__int128`.要超安全`(uintmax_t)1 << x` (5认同)
  • 使用像`-x`这样的操作是不安全的,因为C标准允许有符号整数(例如)符号大小,1'补码或任何其他可以表达必要范围的系统,这意味着`-x`是*不*必须与`(~x)+ 1`相同.这对现代架构来说并不是什么大不了的事,但你永远不知道什么是一个足够聪明的优化编译器将对你的代码做什么. (5认同)
  • 这些类型的解决方案仅在目标变量是整数类型时才有效.可以使用更一般的解决方案来处理其他有用的目标类型,例如阵列. (3认同)
  • 我认为符号中存在问题.假设我有0011(十进制3)并且我想检查**第二个**位是否设置,例如,粗体:0 0**1**1.你如何引用x?根据你的符号,它会是第二位还是第一位,因为如果它是第二位,我认为你的建议是行不通的,例如,因为你将移位1两次,得到100 - 这不会产生第二位我在上面定义.不是吗? (2认同)
  • @Jiashuo李:如果`number`比`int`大和`N`大于或等于位的数量在`int`这个语句将失败.在这种情况下,它甚至会调用未定义的行为.`number = number&〜((uintmax_t)1 << n)| ((uintmax_t型)X << n)的;`是一个通用表达式应该针对number`的`所有尺寸的工作,但可以生成难看和低效的代码为更小的尺寸. (2认同)
  • 与检查有关:为什么不简单地使用"number&0x01"来检查第一位,"number&0x08"用于第四位等等.Imho更漂亮. (2认同)
  • @ 71GA:按位求反(`~`)将所有位反转,因此`~0xFFF0FFFF`为'0x000F0000`.布尔值not(`!`)如果值为非零则给出0,如果值为零则给出1,因此`!0xFFF0FFFF`为`0x00000000`. (2认同)
  • @Kevin:哦,对,应该是` - (unsigned long)x`,它也会回避有符号整数表示.(无符号基数2匹配2的补语语义.)但只有当`x`为0或1时它才能正常工作.记住我们将'n`设置为'x`. (2认同)

Martin York.. 412

使用标准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

标准库编译时大小的bitset相比,Boost版本允许运行时大小的bitset.

  • 也许没有人提到它,因为它被标记为嵌入式.在大多数嵌入式系统中,您可以像瘟疫一样避免STL.在大多数嵌入式编译器中,增强支持可能是一种非常罕见的鸟类. (63认同)
  • @Lundin:你的陈述过于宽泛(因此没有争议).我相信我能找到他们是真实的情况.这不会改变我的初始观点.这两个类都非常适合在嵌入式系统中使用(我知道它们被使用的事实).关于未在嵌入式系统上使用STL/Boost的初步观点也是错误的.我确信有些系统不使用它们,即使是使用它们的系统,它们也会被明智地使用,但是说它们没有被使用是不正确的(因为有系统被使用). (33认同)
  • +1.不是说std​​ :: bitset可用于"C",但是当作者用"C++",AFAIK标记他/她的问题时,你的答案是最好的... std :: vector <bool>是另一种方式,如果一个人知道它的优点和缺点 (27认同)
  • @andrewdotnich:vector <bool>是(遗憾的)一个将值存储为位的特化.有关更多信息,请访问http://www.gotw.ca/publications/mill09.htm ... (15认同)
  • @Martin这是非常真实的.除了像STL和模板这样的特定性能杀手之外,许多嵌入式系统甚至完全避开了整个标准库,因为它们很难验证.大多数嵌入式分支都采用像MISRA这样的标准,这需要静态代码分析工具(任何软件专业人员都应该使用这样的工具,而不仅仅是嵌入式人员).通常人们比通过整个标准库运行静态分析有更好的事情 - 如果它的源代码甚至可以在特定的编译器上使用它们. (15认同)
  • @mloskot:问题清楚地说明了C++.它在问题下面的问题是什么语言是有效的.它获得了超过120个以上的选票,因为可以阅读的人阅读问题并在标签的上下文中正确评估.:-)哦.这很简单. (9认同)
  • @Lundin:第一点不是真的(STL中的某些东西是可以避免的,但像这样的一揽子声明只是一般的,std :: bitset很好并且不需要花费任何费用.).第二点Boost :: dynamic_bitset不依赖于任何东西,可以很容易地使用. (6认同)

Ferruccio.. 224

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

struct bits {
    unsigned int a:1;
    unsigned int b:1;
    unsigned int c:1;
};

struct bits mybits;

定义一个3位字段(实际上,它是三个1位字符).位操作现在变得有点(哈哈)更简单:

设置或清除一下:

mybits.b = 1;
mybits.c = 0;

要切换一下:

mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1;  /* all work */

检查一下:

if (mybits.c)  //if mybits.c is non zero the next line below will execute

这仅适用于固定大小的位字段.否则你必须采用之前帖子中描述的比特技巧.

  • 我总是发现使用位域是一个坏主意.您无法控制分配位的顺序(从顶部或底部),这使得无法以稳定/可移植的方式将值序列化,除了一次一位.将DIY位算术与位域混合也是不可能的,例如制作一次测试几位的掩码.您当然可以使用&&并希望编译器能够正确地优化它... (59认同)
  • 与大多数语言功能一样,位字段可以正确使用,也可以被滥用.如果需要将几个小值打包到单个int中,则位字段非常有用.另一方面,如果您开始假设位字段如何映射到实际的包含int,那么您只是在寻找麻烦. (39认同)
  • 比特字段在很多方面都很糟糕,我几乎可以写一本关于它的书.事实上,我几乎不得不为需要符合MISRA-C标准的现场计划做到这一点.MISRA-C强制执行所有实现定义的行为,因此我最后写了一篇关于位域中可能出错的所有内容的文章.位顺序,字节顺序,填充位,填充字节,各种其他对齐问题,与位字段之间的隐式和显式类型转换,如果未使用int,则为UB,等等.相反,使用按位运算符可以减少错误和可移植代码.位字段完全是冗余的. (31认同)
  • @endolith:那不是个好主意.你可以使它工作,但它不一定可以移植到不同的处理器,或不同的编译器,甚至可以移植到同一编译器的下一个版本. (4认同)
  • @Yasky和Ferruccio对这种方法得到sizeof()的不同答案应该说明不仅在编译器之间而且在硬件之间的兼容性问题.我们有时会欺骗自己,我们已经用语言或定义的运行时解决了这些问题,但它真的归结为"它能在我的机器上运行吗?".你嵌入的家伙有我的尊重(和同情). (3认同)

大智慧.. 145

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

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

  • 呃我意识到这是一个5岁的帖子,但是在这些宏中没有任何重复,Dan (17认同)
  • `BITMASK_CHECK(x,y)((x)&(y))`必须是`((x)&(y))==(y)`否则它会在多位掩码上返回不正确的结果(例如``5` vs .`3`)/*你好所有掘墓人:)*/ (11认同)
  • `1`应该是`(uintmax_t)1`或者类似的,以防任何人试图在`long`或更大的类型上使用这些宏 (7认同)

dmckee.. 107

它有时是值得使用enum命名的位:

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

然后使用名称.即写

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

设置,清除和测试.这样您就可以隐藏其余代码中的幻数.

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

  • @endolith:使用`enum`s作为相关常量集可以追溯到c编程很长的路要走.我怀疑使用现代编译器时,唯一的优势就是"const short"或者其他什么是明确地组合在一起.当你想要它们用于*除了位掩码以外的东西时,你会得到自动编号.当然,在c ++中,它们也形成了不同的类型,为您提供了一些额外的静态错误检查. (3认同)
  • 如果您使用此方法,请记住,枚举常量始终是带符号的类型`int`.由于对带符号类型的隐式整数提升或按位运算,这可能会导致各种微妙的错误.例如,`thingstate = ThingFlag1 >> 1`将调用实现定义的行为.`thingstate =(ThingFlag1 >> x)<< y`可以调用未定义的行为.等等.为安全起见,始终强制转换为无符号类型. (3认同)

大智慧.. 41

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或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(disassembled)] -------------------------------------- ---------

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] ------------------------------------------- ----------------------

  • -1这只是一种奇怪的混淆.永远不要通过隐藏宏背后的语言语法来重新发明C语言,这是非常糟糕的做法.然后是一些奇怪的事情:首先,1L被签名,这意味着所有位操作都将在有符号类型上执行.传递给这些宏的所有内容都将返回为signed long.不好.其次,这对于较小的CPU来说效率非常低,因为当操作可能处于int级别时,它会执行很长时间.第三,类似函数的宏是所有邪恶的根源:你没有任何类型安全.此外,之前关于没有分配的评论非常有效. (15认同)
  • 关于这一点的两件事:(1)在仔细阅读你的宏时,有些人可能错误地认为宏实际上是在arg中设置/清除/翻转位,但是没有赋值; (2)你的test.c不完整; 我怀疑如果你运行更多的案例你会发现问题(读者练习) (3认同)
  • 如果`arg`是'long long`,这将失败.`1L`需要是最广泛的类型,所以`(uintmax_t)1`.(你可以通过`1ull`逃脱) (2认同)

nsanders.. 32

使用按位运算符: & |

要设置最后一位000b:

foo = foo | 001b

检查最后一位foo:

if ( foo & 001b ) ....

清除最后一点foo:

foo = foo & 110b

我用来XXXb表示清晰.您可能正在使用HEX表示,具体取决于您打包位的数据结构.

  • C中没有二进制表示法.二进制整数常量是非标准扩展. (3认同)

kapilddit.. 31

对于初学者,我想用一个例子来解释一下:

例:

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)

| operator:设置位

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


R.... 26

这里是我最喜欢的位算术宏,这对于任何类型的无符号整数数组的作品从unsigned charsize_t(这是应该是有效的一起工作最大的类型):

#define BITOP(a,b,op) \
 ((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))

设置一下:

BITOP(array, bit, |=);

要清楚一点:

BITOP(array, bit, &=~);

要切换一下:

BITOP(array, bit, ^=);

测试一下:

if (BITOP(array, bit, &)) ...

等等

  • 阅读是好的,但应该注意可能的副作用.在循环中使用`BITOP(array,bit ++,| =);`很可能不会执行调用者想要的操作. (4认同)

John U.. 23

由于这被标记为"嵌入式",我假设您正在使用微控制器.所有上述建议都是有效的和工作(读 - 修改 - 写,工会,结构等).

但是,在基于示波器的调试过程中,我惊讶地发现,与直接将值写入微型PORTnSET/PORTnCLEAR寄存器相比,这些方法在CPU周期中具有相当大的开销,这使得存在紧密环路/高电平时会产生真正的差异-frequency ISR的切换引脚.

对于那些不熟悉的人:在我的例子中,micro有一个通用的引脚状态寄存器PORTn,它反映了输出引脚,所以做PORTn | = BIT_TO_SET会导致对该寄存器的读 - 修改 - 写.但是,PORTnSET/PORTnCLEAR寄存器取"1"表示"请将此位1"(SET)或"请将此位置零"(CLEAR),将"0"表示"保持引脚单独".所以,你最终有两个端口地址取决于不管你是设置或清除位(并不总是很方便),但很多更快的反应和更小的汇编代码.


Roddy.. 22

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

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

然后,您可以像以前一样读取,写入,测试各个值.

  • 关于位字段的所有内容都是实现定义的.即使你设法找到有关特定编译器如何实现它们的所有细节,在代码中使用它们肯定会使它不可移植. (2认同)

bill.. 19

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

#define BITS 8
#define BIT_SET(  p, n) (p[(n)/BITS] |=  (0x80>>((n)%BITS)))
#define BIT_CLEAR(p, n) (p[(n)/BITS] &= ~(0x80>>((n)%BITS)))
#define BIT_ISSET(p, n) (p[(n)/BITS] &   (0x80>>((n)%BITS)))

  • `CHAR_BIT`已经由`limits.h`定义,你不需要输入你自己的`BITS`(事实上你通过这样做会使你的代码变得更糟) (2认同)

John Zwinck.. 18

检查任意类型变量中任意位置的位:

#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测试了它.可以制作类似的宏来设置和清除位.与其他许多解决方案相比,此解决方案的主要区别在于它适用于几乎任何类型的变量中的任何位置.


Tim Ring.. 14

如果你做了很多事情,你可能想要使用面具,这将使整个事情变得更快.以下函数非常快并且仍然灵活(它们允许在任何大小的位图中进行位错).

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等,在内存中正确映射(小端处理器比大端处理器"更好"的主要原因啊,我觉得火焰战即将到来上...).

  • 不要将表用于可以使用单个运算符实现的函数.TQuickByteMask [n]相当于(1 << n).此外,让你的论点简短是一个非常糟糕的主意./和%实际上是一个除法,而不是bitshift/bitwise,因为2的幂的有符号除法不能按位实现.你应该让参数类型为unsigned int! (2认同)
  • 对于大/小端,big endian将以相同的方式映射整数和原始数据(例如字符串):从整个位图到左到右msb到lsb.虽然小端将从左到右的整数映射为7-0,15-8,23-18,31-24,但原始数据仍然是从左到右的msb到lsb.因此,对于您的特定算法来说,小端更好的方式完全超出我的范围,它似乎恰恰相反. (2认同)
  • @R ..如果你的平台无法有效地转移,如旧的微芯片设备,表格可能很有用,但当然样本中的划分绝对是低效的 (2认同)

大智慧.. 13

该程序将任何数据位从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

用这个:

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

    return num;
}

  • 好吧,它使用低效的分支. (5认同)
  • @asdf编译器的工作是输出最有效的二进制文件,程序员的工作就是编写清晰的代码 (3认同)
  • 这是测试,设置和清除特定位的良好演示.然而,这是一个非常糟糕的方法来切换一点. (3认同)

kendotwill.. 10

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


Jeegar Patel.. 10

如果你想在Linux内核中使用C编程执行所有操作,那么我建议使用Linux内核的标准API.

请参阅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计算机上都是原子的,并且有助于保持处理器之间的一致性.


大智慧.. 9

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个值.

我曾经使用这种方法找到重复的贷款记录,其中loan_number是ISAM密钥,使用6位数的贷款号作为位数组的索引.野蛮的快速,并在8个月后,证明我们从中获取数据的主机系统实际上是故障.比特阵列的简单性使得它们的正确性非常高 - 例如,与搜索方法相比.

  • 这为每个`bool`至少使用了整个字节的存储空间.对于使用`int`来实现`bool`的C89设置,甚至可能是4个字节 (2认同)
  • @RocketRoy:可能值得改变声称这是"位操作"的例子的句子. (2认同)

大智慧.. 6

使用此处定义的运算符之一.

要设置一个位,用来int x = x | 0x?;在那里?是二进制形式的比特位置.

  • `0x`是十六进制的文字的前缀,而不是二进制. (2认同)

大智慧.. 5

这是我使用的一些宏:

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)


归档时间:

查看次数:

996238 次

最近记录:

9 月 前