非数组类型的指针算术

Pointer arithmetics on non-array types

本文关键字:指针 数组 类型      更新时间:2023-10-16

让我们考虑以下代码段:

struct Blob {
    double x, y, z;
} blob;
char* s = reinterpret_cast<char*>(&blob);
s[2] = 'A';

假设 sizeof(double) 是 8,此代码是否会触发未定义的行为?

引用

N4140(大约C++14):

3.9 类型 [基本类型]

2 对于任何简单可复制类型的对象(基类子对象除外)T,无论该对象是否具有 T 类型的有效值,构成该对象的底层字节 (1.7) 都可以复制到charunsigned char数组中。42 如果charunsigned char数组的内容被复制回对象中,则对象随后应保持其原始值。

42)例如,通过使用库函数(17.6.1.2)std::memcpystd::memmove

3 对于任何平凡可复制的类型T,如果指向T的两个指针指向不同的T对象obj1obj2,其中obj1obj2都不是基类子对象,如果构成obj1的底层字节(1.7)被复制 43 obj2 obj2随后应保持与obj1相同的价值。[ 示例: ... ]

43)例如,通过使用库函数(17.6.1.2)std::memcpystd::memmove

原则上,这确实允许直接分配给s[2],如果您认为间接需要分配给s[2]等效于将所有其他Blob复制到一个数组中,该数组恰好在字节上相同,除了第三个字节,并将其复制到您的Blob: 您不会分配给s[0]s[1]等。对于包括 char 在内的平凡可复制类型,这相当于将它们设置为它们已经具有的确切值,这也没有可观察到的效果。

但是,如果获得s[2] == 'A'的唯一方法是通过内存操作,那么也可以提出一个有效的论点,即您要复制回Blob的内容不是构成任何先前Blob的基础字节。在这种情况下,从技术上讲,行为将不因不作为而定义。

我确实强烈怀疑,特别是考虑到"对象是否具有类型 T 的有效值"注释,它旨在被允许。

标准的第3.10章似乎允许这种特定情况,假设"访问存储的值"意味着"读取或写入",这是不清楚的。

3.10-10

如果程序尝试通过以下方式访问对象的存储值不是以下类型之一的 glvalue 行为是未定义

—(10.1) 对象的动态类型,

—(10.2) 对象的动态类型的 CV 限定版本,

—(10.3) 与 4.4 中的动态类型类似的类型(如 4.4 中所定义)对象

—(10.4) 对应于对象的动态类型,

—(10.5) 一种类型,它是对应于对象的动态类型的 CV 合格版本,

—(10.6) 包含以下之一的聚合或联合类型其元素或非静态数据成员中的上述类型(以递归方式包括子聚合或包含的并集),

—(10.7) 一种类型,它是 (可能符合 CV 条件的)基类类型对象的动态类型,

—(10.8) 字符或无符号字符类型