请考虑以下代码。它没问题还是会导致未定义的行为?
#include <iostream>
int main()
{
{
unsigned char binary[] = {0, 5, 10};
bool* x = reinterpret_cast<bool*>(&binary[0]);
for (unsigned int i = 0; i < 3; ++i)
{
std::cout << (x[i] ? 1 : 0) << " ";
}
}
{
unsigned char b = 255;
bool* x = reinterpret_cast<bool*>(&b);
std::cout << (*x ? 1 : 0) << std::endl;
}
return 0;
}
使用gcc 4.6到4.8编译时的输出是
0 5 10 1
但仅限于优化(-O1
及更多)。
Clang结果
0 1 1 1
即使有优化。
现在,如果将y[i] ? 1 : 0
更改为y[i] ? 2 : 1
gcc结果
1 2 2 1
。
任何想法或者因为演员而只是未定义的行为?
答案 0 :(得分:6)
该标准并不保证bool
与char
完全兼容(即不保证它们具有相同的大小或对齐方式),并说:
bool
类型的值为true
或false
。§3.9.1[basic.fundamental]
它还说:
将本国际标准所描述的方式中的
bool
值用作“未定义”,例如通过检查未初始化的自动对象的值,可能会使其表现得好像既不是true
也不是false
。脚注47(N3337)
因此,您处于未定义行为的范畴。
请注意,由于标准不会使bool
成为例外,因此适用以下规则:
如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:
- 对象的动态类型,
- 对象的动态类型的cv限定版本,
- 与对象的动态类型相似的类型(如4.4中所定义)
- 与对象的动态类型对应的有符号或无符号类型
- 对应于对象动态类型的cv限定版本的有符号或无符号类型,
- 聚合或联合类型,包括其元素或非静态数据成员中的上述类型之一(包括递归地,子聚合或包含联合的元素或非静态数据成员),
- 一种类型,是对象动态类型的(可能是cv限定的)基类类型,
- char或unsigned char类型。
§3.10[basic.lval]
在这种情况下,对象的类型是unsigned char
,因此尝试通过bool
左值(通过解除引用bool *
获得的那个)访问它们会导致UB。
答案 1 :(得分:2)
这违反strict aliasing rule,我会引用我的回答here:
严格的别名规则,这使得访问对象成为非法 通过一个不同类型的指针,虽然通过char访问 * 被允许。允许编译器假设不同类型的指针不指向相同的内存并进行优化 因此。它还意味着代码调用未定义的行为和 真的可以做任何事情。
标准草案在3.10
Lvalues和rvalues 段落 10 中涵盖了这一点:
如果程序试图通过访问对象的存储值 行为是除以下类型之一以外的glvalue 未定义: 52
- 对象的动态类型,
- 对象的动态类型的cv限定版本,
- 与对象的动态类型相似的类型(如4.4中所定义)
- 与对象的动态类型对应的有符号或无符号类型
- 对应于动态类型的cv限定版本的有符号或无符号类型 对象,
- 聚合或联合类型,包括其元素中的上述类型之一或非静态类型 数据成员(包括,递归地,子集合的元素或非静态数据成员 或包含联盟),
- 一种类型,是对象动态类型的(可能是cv限定的)基类类型,
- char或unsigned char类型。
如果这不是问题,则不清楚,尝试将bool解释为char甚至是有效的,3.9.1
基本类型部分说:
bool类型的值为true或false。[...]
脚注 47 表示:
以本国际标准描述的方式将bool值用作“未定义”,例如通过检查a的值 未初始化的自动对象,可能会使其表现为既不是真也不是假。