工程化的布尔值可以与true和false都进行比较,为什么呢?

23
下面的示例编译通过,但输出结果相当奇怪:
#include <iostream>
#include <cstring>

struct A
{
    int a;
    char b;
    bool c;
};

int main()
{
    A v;
    std::memset( &v, 0xff, sizeof(v) );

    std::cout << std::boolalpha << ( true == v.c ) << std::endl;
    std::cout << std::boolalpha << ( false == v.c ) << std::endl;
}

输出是:
true
true

有人能解释一下为什么吗?
如果有关系的话,我正在使用g++ 4.3.0。

1
不要像那样使用memset。它完全忽略了你正在覆盖的类型。 - Neil Kirk
5个回答

11

在C++标准中发现了这个,第3.9.1节"基本类型"(请注意神奇的脚注42):

6. Values of type bool are either true or false. 42)

42) 在国际标准中,如果使用布尔值的方式被描述为“未定义”,例如通过检查未初始化的自动变量的值,则可能导致它的行为表现得既不是真也不是假。

对我来说并不十分清楚,但似乎回答了这个问题。


这意味着它可以是任何东西,比如布尔变量的第三状态。UB就像那样。可能会导致鼻妖精:P - BЈовић
1
@dribeas - David Rodríguez:我认为未初始化的变量只是“未定义”用法的一个例子(请注意“例如”)。 - Roman L
2
结构体中的 bool 成员被 memset 函数赋予了未定义的值。这是停止使用 memset 而改用构造函数的一个很好的例子。 - Thomas Matthews
没有所谓的“工程布尔值”。 - Lightness Races in Orbit
根据我的理解,重点是未初始化只是一个例子。脚注提到了UB,其中包括标准没有定义的内容,并且该脚注链接到一个明确规定“bool”只能保持值“true”或“false”的部分。以这种方式进行memset的“bool”将引发UB。 - underscore_d
显示剩余2条评论

2

覆盖v所使用的内存位置的结果是未定义行为。 根据标准,任何事情都可能发生(包括你的电脑飞走并吃掉你的早餐)。


4
我认为这样做实际上是合法的,因为v是一个POD。 - Puppy
@DeadMG:我认为标准没有特别区分bool。UB就是UB。你有关于布尔值的特别说明参考资料吗? - Vlad
1
结构体A是POD类型,因此您可以随意处理它,包括向占用此类对象的内存位置填充一些随机值。 - BЈовић
3
@VJo: 3.9/2规定,对于任何POD类型T的对象(除了基类子对象),无论该对象是否保存了类型T的有效值,组成对象的底层字节(1.7)都可以被复制到char或unsigned char数组中。如果char或unsigned char数组的内容被复制回该对象,则该对象随后应保持其原始值。它并没有说“你可以随意使用它”,特别是它没有说你可以写入任何字节,并使结果成为对象的有效值(在本例中是bool)。 - Steve Jessop
2
标准存在一个缺陷报告,即它没有正确说明无效值如何导致未定义的行为,但当您将内存读取为不是有效值表示的类型时,它肯定不会定义行为。在您的实现中,“全1”位模式可能不是bool的有效值表示。 - Steve Jessop
显示剩余5条评论

2
一个布尔值,如果它的内存被设置为不是1或0的值,则具有未定义的行为。

11
你能分享一下你参考的资料吗? - Marcin

2
我觉得我找到了答案。3.9.1-6说:
bool类型的值只能是true或者false。注释42中提到,没有带符号、无符号、短或长的bool类型或值。如下所述,bool值的行为类似于整数类型。bool类型的值参与整数提升(4.5)。
其中注释42说:
在国际标准中描述为“未定义”的方式中使用bool值,例如检查未初始化的自动变量的值,可能会导致它表现得既不是true也不是false。

请注意,注释是信息性的,而不是规范性的。无论自动变量的类型是bool还是其他类型,使用任何未初始化的自动变量的值都是未定义的行为。 - Steve Jessop

1

我似乎找不到标准中说明为什么会发生这种情况(很可能是我的问题)——这包括7vies提供的参考资料,但它本身并没有提供太多帮助。这绝对是未定义行为,但我无法解释OP观察到的具体行为。

实际上,我非常惊讶输出结果是这样的。

true
true

使用VS2010,输出更易于解释:

false
false

在后一种情况下,发生的情况如下:
  • 编译器将对布尔值true的比较实现为等于0x01的测试,由于0xff != 0x01,结果为false
  • 对于布尔值false的比较也是如此,只不过与之比较的值现在是0x00

我想不出任何实现细节会导致false在解释为bool时与值0xff相等。有人有关于这个问题的想法吗?


不错的观点,但这确实是与编译器相关的。未定义行为允许编译器进行任何优化,并且对于特定的编译器,这会导致结果为“true”。你的“0xff != 0x01”远非唯一的检查方法... - Roman L
@7vies: 当然。我想我们都同意这是未定义行为,而且你不能使用这段代码来得到有意义的结果。但了解它为什么会这样行为仍然是有趣的。 - Jon
@Jon,例如,可以使用x!= false而不是x == true,这在考虑布尔值时是等效的,但对于整数会产生不同的行为(0xff == 1false,但0xff != 0true)。 - Roman L
@7vies:我们仍然同意所有这些。我只是想知道在这种情况下GCC在做什么,并得出这个结果。 - Jon
@Jon,你的意思是为什么它更喜欢使用 x != false 而不是 x == true 吗?当我在发布模式下查看生成的汇编代码时,通常会有100个类似的问题,为什么它要这样巧妙地处理而不是其他方式。对于所有这些问题都没有答案,这是不可能的 :) - Roman L
6
@Jon:查看GCC的反汇编代码(在我的机器(TM)上),它使用movzbl读取bool,它将复制8位。它不需要实际测试是否为“true”,因为即使没有优化编译器选项,“true ==”也是多余的并被优化掉了。因此,传递了“0xff”。false ==变成一个异或运算符与1相结合,因此传递了“0xfe”。当boolalpha操纵器生效时,我不知道流对该值进行了什么操作,但考虑到结果,我猜测它所执行的操作相当于:如果位模式全为零,则打印“false”,否则打印“true”。 - Steve Jessop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接