工程布尔比较等于真假,为啥?
Posted
技术标签:
【中文标题】工程布尔比较等于真假,为啥?【英文标题】:Engineered bool compares equal to both true and false, why?工程布尔比较等于真假,为什么? 【发布时间】:2010-12-23 12:59:47 【问题描述】:下面的例子可以编译,但是输出很奇怪:
#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
【问题讨论】:
不要那样使用 memset。它完全忽略了您正在写入的类型。 【参考方案1】:在 C++ 标准第 3.9.1 节“基本类型”中找到了这个(注意神奇的脚注 42):
6. Values of type bool are either true or false. 42)
42) 以本国际标准将布尔值描述为“未定义”的方式使用布尔值,例如通过检查未初始化的自动变量的值,可能会导致其表现得既不是真也不是假。
这对我来说不是很清楚,但似乎回答了这个问题。
【讨论】:
这意味着它可以是任何东西,比如布尔变量的第三种状态。 UB就是这样。可能导致鼻恶魔:P 请注意脚注 42 不适用于此处,因为内存已使用 memset 初始化为所有1
s
@David Rodríguez - dribeas:我认为未初始化的变量只是“未定义”用法的一个示例(注意“例如”)
结构中的bool
成员被memset
函数分配了一个未定义的值。这是停止使用memset
并改用构造函数的好例子。
@VJo:未初始化的示例不适用,因为memset
的执行会初始化内存。问题在于初始化本身,因为它没有将变量设置为true
或false
,而是设置为0xff
。【参考方案2】:
覆盖v
使用的内存位置的结果是undefined behaviour。
根据标准,一切都可能发生(包括您的计算机飞走和吃早餐)。
【讨论】:
我认为这样做实际上是合法的,因为 v 是 POD。 @DeadMG:我对此表示怀疑。优化器可能对bool
的可能内容有自己的假设。
结构A是POD,所以你可以对它做任何你喜欢的事情,包括将一些随机值填充到此类对象占用的内存位置。
@VJo: 3.9/2 说,“对于 POD 类型 T 的任何对象(基类子对象除外),无论该对象是否拥有 T 类型的有效值,底层字节(1.7) 组成对象可以复制到char或unsigned char数组中。如果char或unsigned char数组的内容被复制回对象中,该对象随后应保持其原始值。它没有说“你可以用它做任何你喜欢的事情”,特别是它没有说你可以写任何你喜欢的字节并且结果是对象的有效值(在这种情况下,@987654324 @)。
有一个关于标准的缺陷报告,它没有正确说明无效值如何导致未定义的行为,但是当您读取不是有效值的内存时,它肯定没有定义行为您阅读它的类型的表示形式。 All-bits-1 可能不是您的实现中bool
的有效值表示。【参考方案3】:
内存设置为非 1 或 0 值的布尔值具有未定义的行为。
【讨论】:
您能分享一下您对此的参考吗?【参考方案4】:我想我找到了答案。 3.9.1-6 说:
bool 类型的值要么为真,要么 false.42) [注意:没有签名, 无符号、短或长布尔类型或 价值观。 ] 如下所述,bool 值表现为整数类型。 bool 类型的值参与 积分促销 (4.5)。
注释 42 说:
42) 使用布尔值的方式 本国际所描述的 标准为“未定义”,例如 by 检查一个值 未初始化的自动变量, 可能会导致它表现得好像 不真也不假。
【讨论】:
请注意,注释是信息性的,而不是规范性的。使用任何未初始化的自动变量的值都是未定义的行为,无论其类型是bool
还是其他类型。【参考方案5】:
我似乎无法在标准中找到任何说明为什么会发生这种情况的内容(很可能是我的错)——这确实包括 7vies 提供的参考资料,这本身并不是很有帮助。这绝对是未定义的行为,但我无法解释 OP 观察到的具体行为。
作为一个实际问题,我很惊讶输出是
true
true
使用VS2010,输出更容易解释:
false
false
在后一种情况下,会发生什么:
与布尔值true
的比较由编译器实现,作为与0x01
相等性的测试,由于0xff != 0x01
,结果为false
。
与布尔值false
的比较也是如此,只是现在比较的值是0x00
。
当解释为bool
时,我想不出任何会导致false
与值0xff
进行比较的实现细节。有人对此有任何想法吗?
【讨论】:
很好,但这确实是特定于编译器的。 UB 允许它做任何它想要的优化,对于那个特定的编译器,这会导致一个true
。您的0xff != 0x01
远不是检查它的唯一方法......
@7vies:当然。我想我们都同意这是 UB,你不能使用这个代码来产生有意义的结果。但是知道为什么它会这样表现仍然很有趣。
@Jon,例如,代替x == true
,它可以使用x != false
,这在您考虑布尔值时是等效的,但会导致整数的不同行为(0xff == 1
是false
但0xff != 0
是true
)。
@7vies:我们仍然同意所有这些。我只是想知道 GCC 在这种情况下做了什么并得出了这个结果。
@Jon:查看来自 GCC 的反汇编(在我的机器(TM)上),它使用 movzbl
读取 bool
,它复制 8 位。它不需要针对true
进行实际测试,因为true ==
是多余的,并且即使没有优化编译器选项也会被优化掉。所以0xff
通过了。 false ==
与 1 成为异或,因此 0xfe
被传递。我不知道当 boolalpha 操纵器生效时流对值做了什么,但鉴于结果我猜想相当于:如果位模式是全零打印“假”,否则打印“真”。跨度>
以上是关于工程布尔比较等于真假,为啥?的主要内容,如果未能解决你的问题,请参考以下文章