C 将位与布尔值进行比较

C 将位与布尔值进行比较,c,gcc,boolean,bitwise-operators,C,Gcc,Boolean,Bitwise Operators,假设我有一组标志,用uint16\t标志编码。例如,AMAZING_FLAG=0x02。 现在,我有一个函数。这个函数需要检查我是否想更改标志,因为如果我想这样做,我需要写入flash。这很昂贵。因此,我需要一个检查,它告诉我flags&mazing_FLAG是否等于doSet。这是第一个想法: setAmazingFlag(bool doSet) { if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0)) {

假设我有一组标志,用uint16\t
标志编码。例如,
AMAZING_FLAG=0x02
。 现在,我有一个函数。这个函数需要检查我是否想更改标志,因为如果我想这样做,我需要写入flash。这很昂贵。因此,我需要一个检查,它告诉我
flags&mazing_FLAG
是否等于
doSet
。这是第一个想法:

setAmazingFlag(bool doSet)
{
    if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0)) {
        // Really expensive thing
        // Update flags
    }
}
这不是一个直观的if语句。 我觉得应该有更好的方法,比如:

if ((flags & AMAZING_FLAG) != doSet){

}
但这实际上不起作用,
true
似乎等于
0x01


那么,有没有一种简洁的方法可以将位与布尔值进行比较呢?

从逻辑的角度来看,
flags&mazing_FLAG
只是一种位操作,它屏蔽了所有其他标志。结果是一个数值

要接收到布尔值,可以使用比较

(flags & AMAZING_FLAG) == AMAZING_FLAG
现在可以将此逻辑值与
doSet
进行比较

if (((flags & AMAZING_FLAG) == AMAZING_FLAG) != doSet)
在C语言中,由于数字到布尔值的隐式转换规则,可能会有缩写。所以你也可以写

if (!(flags & AMAZING_FLAG) == doSet)

写得更简洁。但是前一个版本在可读性方面更好。

要将任何非零数字转换为1(true),有一个老技巧:应用
(非)运算符两次

if (!!(flags & AMAZING_FLAG) != doSet){

您需要将位掩码转换为布尔语句,这在C中相当于值
0
1

  • (旗帜和神奇旗帜)!=0
    。最常见的方式

  • !!(标志和神奇标志)
    。有点普通,也可以使用,但有点神秘

  • (bool)(旗帜和惊艳旗帜)
    。仅从C99和更高版本开始的现代C方式

选择上面的任意一个选项,然后使用
将其与布尔值进行比较=
==
您可以基于
doSet
值创建掩码:

#define AMAZING_FLAG_IDX 1
#define AMAZING_FLAG (1u << AMAZING_FLAG_IDX)
...

uint16_t set_mask = doSet << AMAZING_FLAG_IDX;
#定义惊人的_标志_IDX 1

#定义惊人的_标志(1u执行此测试有多种方法:

三元运算符可能会产生代价高昂的跳跃:

if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0))
您还可以使用布尔转换,这可能有效,也可能无效:

if (!!(flags & AMAZING_FLAG) != doSet)
或其等效替代方案:

if (((flags & AMAZING_FLAG) != 0) != doSet)
if ((flags & AMAZING_FLAG) != (-doSet & AMAZING_FLAG))
如果乘法很便宜,您可以使用以下方法避免跳跃:

if ((flags & AMAZING_FLAG) != doSet * AMAZING_FLAG)
如果
标志
为无符号且编译器非常智能,则下面的除法可能会编译为一个简单的移位:

if ((flags & AMAZING_FLAG) / AMAZING_FLAG != doSet)
如果体系结构使用2的补码算法,那么还有另一种选择:

if (((flags & AMAZING_FLAG) != 0) != doSet)
if ((flags & AMAZING_FLAG) != (-doSet & AMAZING_FLAG))
或者,可以将
标志
定义为具有位字段的结构,并使用更简单的可读语法:

if (flags.amazing_flag != doSet)

遗憾的是,这种方法通常不受欢迎,因为位字段的规范不允许对位级实现进行精确控制。

不完全清楚您的逻辑需要是什么。是这样的:
(flags&mazing_FLAG)&&doSet
?问题还不清楚。我们想检查“标志”是否为0x01。这是您想要的吗?如果是,那么我们可以使用位运算符“&”。如果您想让它更具可读性,请仅在doSet为真时调用setAmazingFlag,那么函数名会更好地签出,否则您的函数可能会执行或可能不会执行nam的操作e说,会导致糟糕的代码阅读如果你只是想让它更可读,只需要做一个函数'flagsNotEqual`。或者
(bool)(flags&sazing_FLAG)!=doSet
——我觉得更直接。(尽管这表明微软先生对此有问题。)另外,
((flags&sazing_FLAG)!=0)
可能正是它编译的目的,并且是绝对明确的。“而且,((flags&mazing_FLAG)!=0)可能正是它编译的目的,并且是绝对明确的。”会吗?如果doSet是真的呢?@ChrisHall(bool)2导致1还是仍然是2,在C?C11标准中,6.3.1.2布尔类型:“当任何标量值转换为_Bool时,如果该值比较等于0,则结果为0;否则,结果为1。”。和6.5.4强制转换运算符:“在表达式前面加括号的类型名称将表达式的值转换为命名类型。”@Cheiron,更清楚地说:
((Bool)(flags&sazing_FLAG)!=doSet)
的效果与
(((flags&mazing_FLAG)!=0)!=doSet)
相同,它们可能编译成完全相同的东西。建议的
(!!(flags&mazing_FLAG)!=doSet)
是等效的,我想也会编译成同样的东西。这完全取决于你认为哪一个更清晰:演员需要你记住演员是如何转换成布尔的;
!!
需要一些其他的心理练习,或者你要知道“把戏”"。所有形式的布尔表达式都有可能导致分支。在应用奇怪的手动优化技巧之前,我会先反汇编。这主要是编译器的优化器的问题,而不是ISA本身的问题。@Lundin我不同意。有些体系结构有ISA用于条件位设置/重置,在这种情况下不需要分支,其他注意:如果您这样做,请根据
惊奇标志\u IDX
(例如
#定义惊奇标志((uint16\u t))定义
惊奇标志(1@AlexLop。不确定我将在该链接中看到什么。对我来说,两种体系结构的两个函数都有一个条件分支。任何代码的主要目标都应该是可读性,而大多数示例都不是。当您实际将示例加载到编译器中时,会出现一个更大的问题:最初的两个示例imp元素具有最好的性能:最少的跳转和加载(ARM 8.2,-Os)(它们是等效的)。所有其他实现的性能都更差。一般来说,应该避免做花哨的事情(微优化),因为这样会使编译器更难弄清楚发生了什么,从而降低性能。因此,-1。