请考虑以下程序。
对于最近的gcc,所有比较都是正确的,但只有值1与x86的Visual Studio命令行编译器v.19.16.27031.1比较。
我认为通常可以通过char指针写入POD。但是标准中是否有关于将有趣的值写入bool变量的措辞?如果允许,是否有关于比较行为的文字?
#include <iostream>
using namespace std;
void f()
{
if(sizeof(bool) != 1)
{
cout << "sizeof(bool) != 1\n";
return;
}
bool b;
*(char *)&b = 1;
if(b == true) { cout << (int) *(char *)&b << " is true\n"; }
*(char *)&b = 2;
if(b == true) { cout << (int) *(char *)&b << " is true\n"; }
*(char *)&b = 3;
if(b == true) { cout << (int) *(char *)&b << " is true\n"; }
}
int main()
{
f();
}
P.S。 gcc 8.3使用test
指令有效地检查非零值,而gcc 9.1显式地将其与1进行比较,从而仅使该比较为真。也许这个godbolt link有用。
答案 0 :(得分:3)
否。这不行。
在布尔中写入任意数据非常费力(请参见What is the strict aliasing rule?),类似于Does the C++ standard allow for an uninitialized bool to crash a program?
*(char *)&b = 2;
这种类型的punning hack调用UB。根据{{1}}的编译器实现及其允许的优化,您可能拥有demons flying off your nose。
答案 1 :(得分:2)
考虑:
bool b;
b = char{2}; // 1
(char&)b = 2; // 2
*(char*)&b = 2; // 3
在这里,第2行和第3行的含义相同,但第1行的含义不同。在第1行中,由于分配给bool
对象的值不为零,因此保证结果为true
。但是,在第2行和第3行中,bool
对象的对象表示被直接写入。
通过const
类型的左值写入任何非char
类型的对象确实是合法的,但是:
在C ++ 17中,该标准未指定bool
对象的表示形式。 bool
类型可能具有填充位,甚至可能大于char
。因此,以这种方式直接写入bool
值的任何尝试都可能产生无效的(或“陷阱”)对象表示,这意味着随后读取该值将产生不确定的行为。实现可以(但不是标准要求的)定义bool
对象的表示形式。
在C ++ 20中,我的理解是,由于P1236R1,不再有陷阱表示,但是bool
的表示仍未完全指定。 bool
对象可能仍然大于char
,因此,如果仅写入对象的第一个字节,则它仍可以包含不确定的值,并在访问时产生UB。如果bool
是1个字节(可能),则结果不确定-它必须产生基础类型的一些有效值(很可能是char
或其有符号或无符号的表亲),但仍未指定此类值到true
和false
的映射。
答案 2 :(得分:1)
可以将true
和false
以外的值分配给类型bool
的变量。
在分配值之前,通过使用标准转换顺序bool
/ true
将RHS转换为false
。
但是,您尝试执行的操作不正确。
*(char *)&b = 2; // Not OK
*(char *)&b = 3; // Not OK
即使使用该机制分配1和0也不行。
*(char *)&b = 1; // Not OK
*(char *)&b = 0; // Not OK
以下语句可以。
b = 2; // OK
b = 3; // OK
来自the standard/basic.types#basic.fundamental-6:
布尔类型的值为
true
或false
。
该标准未强制将true
表示为1
和/或false
表示为0
。实现可以选择最适合其需求的表示形式。
关于bool
类型的值的standard goes on to say this:
以本国际标准描述为“未定义”的方式使用
bool
值,例如通过检查未初始化的自动对象的值,可能会导致它的行为就好像它们都不是true
也false
。
将值char(1)
或char(0)
间接存储在其存储位置中并不能保证将值正确转换为true
/ false
。由于这些值在实现中可能无法表示true
或false
,因此访问这些值将导致未定义的行为。
答案 3 :(得分:1)
通过指向除bool
之外的类型的指针将任何整数值写入bool
是未定义的行为,因为它们可能与编译器对该类型的表示不匹配。是的,写0或1之外的东西绝对会破坏事情:编译器通常依赖于布尔true
的确切内部表示。
但是bool b = 3
很好,只需将b
设置为true
(将整数类型转换为bool
的规则是,任何非零值都将变为{{1} },零变成true
)。
答案 4 :(得分:0)
通常,最好为bool分配0或1以外的值:
7.3.14布尔转换 [转换布尔] 1算术,无作用域枚举,指针或指针成员类型的prvalue可以转换为bool类型的prvalue。零值,空指针值或空成员指针值将转换为false;其他任何值都将转换为true。
但是您的转换完全是另一个问题。
请小心,认为可以通过指向其他内容的指针来写类型。您可以获得非常令人惊讶的结果,并且允许优化器假设某些事情没有完成。我不知道它的所有规则,但是优化器并不总是遵循指向不同类型的指针的写入操作(在存在未定义行为的情况下,它可以做各种事情!)但是请注意,这样的代码:
bool f()
{
bool a = true;
bool b = true;
*reinterpret_cast<char*>(&a) = 1;
*reinterpret_cast<char*>(&b) = 2;
return a == b;
}
实时:https://godbolt.org/z/hJnuSi
经过优化:
g ++:-> true
(但实际上是2)
叮当声:-> false
main() {
std::cout << f() << "\n"; // g++ prints 2!!!
}
尽管f()返回布尔值,但g ++实际上在这里main中打印出2
。可能没想到。