奇怪的C ++布尔铸造行为(true!= true)

时间:2009-08-09 20:06:21

标签: c++ casting boolean unions

请阅读内部大学主题:

#include <iostream>
using namespace std;

union zt
{
 bool b;
 int i;
};

int main()
{
 zt w;
 bool a,b;
 a=1;
 b=2;
 cerr<<(bool)2<<static_cast<bool>(2)<<endl;                      //11
  cerr<<a<<b<<(a==b)<<endl;                                      //111
 w.i=2;
 int q=w.b;
 cerr<<(bool)q<<q<<w.b<<((bool)((int)w.b))<<w.i<<(w.b==a)<<endl; //122220
 cerr<<((w.b==a)?'T':'F')<<endl;                                 //F
}

所以abw.b都声明为boola已分配1b已分配2w.b的内部代表更改为2(使用{{1} }})。

这样unionab的所有内容都为w.b,但truea不相等,所以这可能意味着宇宙被破坏了(w.b

我知道这个问题更具理论性而不是实际问题(一个程序员不想改变true!=true的内部表示),但这里有一些问题:

  1. 这没关系吗? (这是用g ++ 4.3.3测试的)我的意思是,如果编译器意识到在布尔比较期间任何非零值可能意味着是真的吗?
  2. 你知道这个角落案件可能成为一个真正的问题吗? (例如,从流中加载二进制数据时)
  3. 编辑:

    三件事:

    1. boolbool有不同的尺寸,没关系。但是如果我使用int代替char该怎么办?或者在int

    2. 请尽可能回答我提出的两个问题。我实际上也对第二个问题的答案感兴趣,因为在我看来,在嵌入式系统(可能是8位系统)中,这可能是一个真正的问题(或不是)。

    3. 新问题:这个真的是未定义的行为吗?如果是,为什么?如果没有,为什么?对规范中的布尔比较运算符没有任何假设吗?

8 个答案:

答案 0 :(得分:17)

如果您读取的联盟成员与编写的最后一个成员的成员不同,那么您将获得未定义的行为。编写一个int成员然后读取union的bool成员可能会导致程序中任何后续点发生任何事情。

唯一的例外是联合是结构的联合,所有结构都包含一个共同的初始序列,在这种情况下可以读取公共序列。

答案 1 :(得分:9)

通常,在为bool分配任意值时,编译器会为您转换它:

int x = 5;
bool z = x; // automatic conversion here

编译器生成的等效代码看起来更像:

bool z = (x != 0) ? true : false;

但是,编译器只会执行一次此转换。假设bool变量中的任何非零位模式等同于true,特别是对于执行等逻辑操作,这是不合理的。生成的汇编代码很笨重。

可以说,如果您使用union数据结构,您就知道自己在做什么,并且能够混淆编译器。

答案 2 :(得分:9)

  
      
  1. 这没关系吗? (这是用g ++ 4.3.3测试的)我的意思是,如果编译器知道在布尔比较期间任何非零值可能意味着是真的吗?
  2.   

任何非零的整数值(或非NULL的指针)都表示true。 但是当比较整数和bool时,bool在比较之前被转换为int。

  
      
  1. 你知道这个角落案件可能成为一个真正的问题吗? (例如,当从流中二进制加载数据时)
  2.   

这始终是一个真正的问题。

  
      
  1. 这可以吗?

         

    我不知道规格是否具体说明了这一点。编译器可能总是创建如下代码:((a!= 0)&amp;&amp;(b!= 0))|| ((a == 0)&amp;&amp;(b == 0))比较两个布尔值时,虽然这可能会降低性能。

         

    在我看来,这不是一个错误,而是一个未定义的行为。虽然我认为每个实现者都应该告诉用户在实现中如何进行布尔比较。

  2.   

如果我们按你的最后一个代码示例,a和b都是bool并通过分别指定1和2来设置为true(Noe 1和2消失,它们现在只是真的)。​​

所以打破你的表达:

a!=0      // true (a converted to 1 because of auto-type conversion)
b!=0      // true (b converted to 1 because of auto-type conversion)

((a!=0) && (b!=0)) => (true && true)  // true ( no conversion done)

a==0      // false (a converted to 1 because of auto-type conversion)
b==0      // false (b converted to 1 because of auto-type conversion)

((a==0) && (b==0)) => (false && false) // false ( no conversion done)

((a!=0) && (b!=0)) || ((a==0) && (b==0)) => (true || false) => true

所以我总是希望上面的表达式能够很好地定义并且总是正确的。

但我不确定这是如何适用于您原来的问题。将整数赋给bool时,整数将转换为bool(如上所述)。 true的实际表示不是由标准定义的,可以是适合bool的任何位模式(您可能不会假设任何特定的位模式)。

当将bool与int进行比较时,首先将bool转换为int,然后进行比较。

  
      
  1. 任何真实案例

         

    如果有人从文件中读取二进制数据到具有bool成员的结构中,那么我脑海中唯一会出现这种情况。问题可能会上升,如果文件是用另一个编写2而不是1的程序编写到bool的位置(可能是因为它是用另一种编程语言编写的)。

         

    但这可能意味着糟糕的编程习惯。

  2.   

在没有知识的情况下,以二进制格式写入数据是不可移植的 每个物体的尺寸都有问题 代表性存在问题:

  • 整数(有结束)
  • Float(表示未定义(通常取决于底层硬件))
  • Bool(标准未定义二进制表示)
  • 结构(成员之间的填充可能不同)

通过所有这些,您需要了解底层硬件和编译器。不同的编译器或不同版本的编译器甚至是具有不同优化标志的编译器可能对上述所有内容都有不同的行为。

Union的问题

struct X
{
    int  a;
    bool b;
};

当人们提到写'a'然后从'b'读取时未定义 原因:因为我们不知道在这个硬件上如何表示'a'或'b'。写入'a'将填写'a'中的位,但这如何反映'b'中的位。如果你的系统使用1字节bool和4字节int,高内存中低内存最高字节中的最低字节,那么将1写入'a'将把1放入'b'。但那么你的实现如何代表一个博尔?真实的代表是1还是255?如果你在'b'中加一个1并且对于所有其他使用的true,它会使用255?

会发生什么

因此,除非您了解硬件和编译器,否则行为将是意外的。

因此,这些用途未定义,但标准不允许。它们被允许的原因是你可能已经完成了研究,并发现在你的系统中使用这个特定的编译器你可以通过做出这些假设来做一些freeky优化。但请注意,假设中的任何更改都会破坏您的代码。

同样,在比较两种类型时,编译器会在比较之前进行一些自动转换,请记住在比较之前将两种类型转换为相同的类型。为了比较整数和bool,将bool转换为整数,然后与另一个整数进行比较(转换将false转换为0,将true转换为1)。如果要转换的对象都是bool,则不需要转换,并且使用布尔逻辑进行比较。

答案 3 :(得分:2)

布尔值是一个字节,整数是四个字节。当您为整数分配2时,第四个字节的值为2,但第一个字节的值为0.如果从联合中读取布尔值,它将获取第一个字节。

编辑:D'哦。正如Oleg Zhylin指出的那样,这仅适用于大端CPU。谢谢你的纠正。

答案 4 :(得分:1)

我相信你所做的是所谓的打字: http://en.wikipedia.org/wiki/Type_punning

答案 5 :(得分:0)

奇怪的是,我从键盘输出不同的输出:

  

11
  111个
  122222个
  T

代码对我来说似乎也是对的,也许这是一个编译错误? See here

答案 6 :(得分:0)

只是写下我的观点:

  1. 这可以吗?

    我不知道规格是否具体说明了这一点。编译器可能总是创建如下代码:((a!= 0)&amp;&amp;(b!= 0))|| ((a == 0)&amp;&amp;(b == 0))比较两个布尔值时,虽然这可能会降低性能。

    在我看来,这不是一个错误,而是一个未定义的行为。虽然我认为每个实现者都应该告诉用户在实现中如何进行布尔比较。

  2. 任何真实案例

    如果有人从文件中读取二进制数据到具有bool成员的结构中,那么我脑海中唯一会出现这种情况。问题可能会上升,如果文件是用另一个编写2而不是1的程序编写到bool的位置(可能是因为它是用另一种编程语言编写的)。

    但这可能意味着糟糕的编程习惯。

  3. 还有一个问题:在嵌入式系统中,这个bug可能是一个比“普通”系统更大的问题,因为程序员通常会做更多的“魔术”来完成工作。

答案 7 :(得分:-1)

解决提出的问题,我认为行为是可以的,在现实世界中不应该成为问题。由于我们在C ++中没有^^,我建议!bool ==!bool作为一种安全的bool比较技术。

这样,bool变量中的每个非零值都将转换为零,并且每个零都会转换为某个非零值,但对于任何否定操作,最可能是一个且相同。