11

只需阅读内部大学线程:

#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被声明为boolais assigned 1bis assigned 2,内部表示w.b变为2(使用 a union)。

这样一来,所有的,将是,但和a不会是相等b的,所以这可能意味着宇宙被打破了()w.btrueaw.btrue!=true

我知道这个问题比实际问题更具理论性(清酒程序员不想更改 a 的内部表示bool),但这里有一些问题:

  1. 这个可以吗?(这是用 g++ 4.3.3 测试的)我的意思是,编译器是否应该知道在布尔比较期间任何非零值都可能意味着真的?
  2. 您知道这种极端情况可能成为真正问题的任何情况吗?(例如,从流中加载二进制数据时)

编辑:

三件事:

  1. bool并且int有不同的尺寸,没关系。但是如果我使用char而不是int. 还是什么时候sizeof(bool)==sizeof(int)

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

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

4

8 回答 8

17

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

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

于 2009-08-09T20:14:41.007 回答
9

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

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

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

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

但是,编译器只会进行一次这种转换。假设bool变量中的任何非零位模式都等价于是不合理的true,尤其是对于像and这样的逻辑运算。生成的汇编代码会很笨拙。

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

于 2009-08-09T20:12:12.777 回答
9
  1. 这个可以吗?(这是用 g++ 4.3.3 测试的)我的意思是,编译器是否应该知道在布尔比较期间任何非零值都可能意味着真的?

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

  1. 您知道这种极端情况可能成为真正问题的任何情况吗?(例如,当从流中二进制加载数据时)

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

  1. 这个可以吗?

    我不知道规范是否对此有任何规定。编译器可能总是创建这样的代码: ((a!=0) && (b!=0)) || ((a==0) && (b==0)) 比较两个布尔值时,尽管这可能会降低性能。

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

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

所以分解你的表达:

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 与 int 进行比较时,首先将 bool 转换为 int,然后进行比较。

  1. 任何真实案例

    我唯一想到的就是,如果有人将二进制数据从文件读取到结构中,那么它有 bool 成员。如果该文件是使用其他程序创建的,该程序将 2 而不是 1 写入 bool 的位置(可能是因为它是用另一种编程语言编写的),则问题可能会出现。

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

没有知识,以二进制格式写入数据是不可移植的。
每个对象的大小都有问题。
表示存在问题:

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

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

联盟的问题

struct X
{
    int  a;
    bool b;
};

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

因此,除非您同时了解硬件和编译器,否则行为将出乎意料。

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

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

于 2009-08-09T21:34:25.230 回答
2

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

编辑:哦。正如 Oleg Zhylin 所指出的,这仅适用于大端 CPU。感谢您的指正。

于 2009-08-09T20:23:44.390 回答
1

我相信你在做什么被称为类型双关语: http ://en.wikipedia.org/wiki/Type_punning

于 2009-08-09T20:20:31.700 回答
0

嗯奇怪,我从键盘得到不同的输出:

11
111
122222
电话

该代码对我来说似乎也是正确的,也许是编译器错误?
看这里

于 2009-08-09T20:11:26.427 回答
0

只是写下我的观点:

  1. 这个可以吗?

    我不知道规范是否对此有任何规定。编译器可能总是创建这样的代码: ((a!=0) && (b!=0)) || ((a==0) && (b==0)) 比较两个布尔值时,尽管这可能会降低性能。

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

  2. 任何真实案例

    我唯一想到的就是,如果有人将二进制数据从文件读取到结构中,那么它有 bool 成员。如果该文件是使用其他程序创建的,该程序将 2 而不是 1 写入 bool 的位置(可能是因为它是用另一种编程语言编写的),则问题可能会出现。

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

还有一点:在嵌入式系统中,这个错误可能比在“普通”系统上更大,因为程序员通常会做更多的“位魔术”来完成工作。

于 2009-08-09T20:27:17.677 回答
-1

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

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

于 2009-08-09T20:38:58.480 回答