bool变量如何不等于True和False?

时间:2016-01-29 01:53:42

标签: c embedded

根据这个问题的接受答案 What is the benefit of terminating if … else if constructs with an else clause?

有一个腐败案例(在嵌入式系统中)会导致一个bool变量 (这是1位) 与True和False都不同,这意味着可以覆盖此代码中的else路径而不是死代码。

if (g_str.bool_variable == True) {
 ...
}
else if (g_str.bool_variable == False) {
 ...
}
else {
 //handle error
}

我试图找出答案,但仍然没有任何线索。

有可能吗?如何?

编辑:为了更清楚,我会给出bool变量的声明,如:

struct {
   unsigned char bool_variable : 1;

} g_str;

并定义:

#define True 1
#define False 0

4 个答案:

答案 0 :(得分:2)

unsigned char bool_variable : 1不是布尔变量。它是1位整数位域。 _Bool bool_variable是一个布尔变量。

  

位字段的类型应为_Boolsigned intunsigned int或其他实现定义类型的限定或非限定版本。它是实现定义的,是否允许原子类型。 > C11dr§6.7.2.1

所以马上unsigned char bool_variable : 1,如果允许的话,它是实现定义的。

如果此类实现处理unsigned char位字段(如int位字段,unsigned char范围可以适合int范围,那么1位会出现问题int位字段。如果1位int位字段采用0, 10, -1的值,则实现定义。这会导致此//handle error块的if()子句。

if (g_str.bool_variable == True) { // same as if (g_str.bool_variable == 1)
 ...
}
else if (g_str.bool_variable == False) { // same as if (g_str.bool_variable == 0)
 ...
}
else {
 //handle error
}

解决方案是简化if()测试:

if (g_str.bool_variable) {
 ...
}
else  {
 ...
}

对于位字段,它是C中的一个角,其中unsigned intsigned int不同,但int位字段小于int的整个宽度可以视为signed intunsigned int。对于位字段,最好是明确的并使用_Boolsigned intunsigned int。注意:使用unsignedunsigned int同义。

答案 1 :(得分:1)

此代码可能具有竞争条件。问题的严重程度将取决于编译器在编译此代码时发出的确切内容。

这里可能会发生什么。您的代码首先检查bool_variable == True,其评估为false。执行会跳过第一个块并跳转到else if。然后,您的代码会检查bool_variable == False,它也会评估为false,因此您将进入最终的else。您正在对bool_variable进行两次离散测试。其他东西(例如另一个线程或ISR)可能会在第一次测试运行之后之前的短暂时间窗口内改变bool_variable的值,之前 第二次测试。

您可以使用if (bool == True) {} else {}而不是重新测试false来完全避免此问题。该版本只会检查一次值,从而消除可能发生损坏的窗口。单独的False检查在第一时间并没有真正为您买任何东西,因为根据定义,一位宽的字段只能采用两个可能的值,因此!True必须与False。即使您使用的是较大的布尔类型,在技术上可以采用两个以上的离散值,您应该使用它,就像它只能有两个(例如0 = false,其他一切= True)。

但这暗示了一个更大的问题。即使只有一个变量检查​​而不是两个,你有一个线程读取变量,另一个线程几乎同时改变它。在True检查之前发生的腐败可能仍会给您带来错误的结果,但更难以发现。您需要某种锁定机制(互斥锁,自旋锁等)以确保一次只有一个线程访问该字段。

然而,唯一可以证明这一点的方法是使用调试器或硬件探测器逐步完成它,并观察两个测试之间的值变化。如果这不是一个选项,您可以通过将else if更改为if并在每个测试之前存储bool_variable的值来解除块的分离。只要两者不同,那么外部的东西就会破坏你的价值。

答案 2 :(得分:1)

你定义事物的方式,这不会发生在x86上。但可能compiler/cpu组合发生。

考虑以下针对if-else-else构造的假设汇编代码。

    mv SP(0), A             # load 4 bytes from stack to register A  
    and A, 0x1              # isolate bit 1 i.e. bool_variable
    cmp A, 0x1              #  compare to 1 i.e. True
    jmp if equal L1
    cmp A, 0x0              #  compare to 0 i.e. False
    jmp if equal L2
    <second else block>
    jmp L3
L1:
    <if block>
    jmp L3
L2:
    <first else block>
L3:
    <code>

现在考虑其中一些说明的假设机器代码。

             opcode-register-value   machine-code   corrupted-code
and A, 0x1     01      03      01      010301          010303
cmp A, 0x1     02      03      01      020301          020302
cmp A, 0x0     02      03      00      020300          020304

上面显示的一个或多个位损坏将导致代码执行第二个else块。

答案 3 :(得分:1)

我写这个例子的原因是,使用&#34; mybool&#34;,FALSETRUE,表明这是一个非标准/预标准的布尔值类型。

在C获得布尔类型的语言支持之前,您将发明自己的布尔类型,如下所示:

typedef { FALSE, TRUE } BOOL;

或可能:

#define FALSE 0
#define TRUE  1
typedef unsigned char BOOL;

在任何一种情况下,你都会得到一个大于1位的BOOL类型,因此可以是0,1或其他类型。

如果我使用stdbool bool / _Boolfalsetrue编写相同的示例,那么它就没有任何意义。因为编译器可能会将代码实现为位字段,而单个位只能具有值1或0。

回想起来,使用防御性编程的一个更好的例子可能是这样的:

typedef enum
{
  APPLES,
  ORANGES
} fruit_t;

fruit_t fruit;

if(fruit == APPLES)
{
  // ...
}
else if(fruit == ORANGES)
{
  // ...
}
else
{
  // error
}