让我们考虑以下代码:
struct Blob {
double x, y, z;
} blob;
char* s = reinterpret_cast<char*>(&blob);
s[2] = 'A';
假设 sizeof(double) 为 8,这段代码会触发未定义的行为吗?
让我们考虑以下代码:
struct Blob {
double x, y, z;
} blob;
char* s = reinterpret_cast<char*>(&blob);
s[2] = 'A';
假设 sizeof(double) 为 8,这段代码会触发未定义的行为吗?
引用 N4140(大约 C++14):
3.9 类型 [basic.types]
2 对于普通可复制类型的任何对象(基类子对象除外)
T
,无论该对象是否拥有类型的有效值T
,构成该对象的底层字节(1.7)都可以复制到char
或的数组中unsigned char
。42char
如果或数组的内容unsigned char
被复制回对象,则该对象随后应保持其原始值。42) 例如,通过使用库函数 (17.6.1.2)
std::memcpy
或std::memmove
.3 对于任何可简单复制的类型
T
,如果两个指针T
指向不同的T
对象,obj1
并且obj2
既不是基类子对象obj1
也不obj2
是基类子对象,如果构成的基础字节(1.7)obj1
被复制到obj2
中,则43obj2
随后应保持与 相同的值obj1
。[示例: ...]43) 例如,通过使用库函数 (17.6.1.2)
std::memcpy
或std::memmove
.
原则上,这确实允许直接赋值,s[2]
如果您采取s[2]
间接要求赋值的位置等同于将所有其他内容复制Blob
到一个数组中,该数组恰好除了第三个字节之外是按字节相同的,并将其复制到your Blob
: 您没有分配给s[0]
,s[1]
等。对于包括 在内的普通可复制类型char
,这相当于将它们设置为它们已经拥有的确切值,这也没有明显的效果。
但是,如果获取的唯一方法s[2] == 'A'
是通过内存操作,那么也可以提出一个有效的论点,即您复制回您Blob
的不是构成任何 previous 的基础字节Blob
。在这种情况下,从技术上讲,行为将因遗漏而未定义。
我确实强烈怀疑,特别是考虑到“对象是否具有类型的有效值T
”注释,它是被允许的。
标准的第 3.10 章似乎允许这种特定情况,假设“访问存储的值”是指“读取或写入”,这还不清楚。
3.10-10
如果程序尝试通过非下列类型之一的泛左值访问对象的存储值,则行为未定义:
—(10.1)对象的动态类型,
—(10.2) 对象动态类型的 cv 限定版本,
—(10.3)与对象的动态类型类似(如 4.4 中定义)的类型,
—(10.4) 一种类型,它是与对象的动态类型相对应的有符号或无符号类型,
—(10.5) 有符号或无符号类型,对应于对象动态类型的 cv 限定版本,
—(10.6)聚合或联合类型,在其元素或非静态数据成员(递归地包括子聚合或包含联合的元素或非静态数据成员)中包括上述类型之一,
—(10.7) 一种类型,它是对象的动态类型的(可能是 cv 限定的)基类类型,
—(10.8) char 或 unsigned char 类型。