【问题标题】:Pointer arithmetics on non-array types非数组类型的指针算法
【发布时间】:2016-01-19 14:51:27
【问题描述】:

让我们考虑以下代码:

struct Blob {
    double x, y, z;
} blob;

char* s = reinterpret_cast<char*>(&blob);
s[2] = 'A';

假设 sizeof(double) 为 8,这段代码会触发未定义的行为吗?

【问题讨论】:

  • 如果没有强制转换,这甚至无法编译。
  • 这是C吗?在 C++ 中,这不会编译。
  • 这不是合法的 C 或 C++,你需要一个演员表。一旦你有了演员表,这是合法的并且定义明确的。当然,如果您之后尝试访问 blob.x,任何事情都可能发生。
  • @Bathsheba,添加了演员,问题仍然存在。
  • @NicolBolas s[2] = *(s+2)

标签: c++ language-lawyer type-punning char-pointer


【解决方案1】:

引自 N4140(大致 C++14):

3.9 类型 [basic.types]

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) 进入obj2,43obj2 应随后保持与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 类型的有效值”评论,它是被允许的。

【讨论】:

【解决方案2】:

标准的第 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 类型

【讨论】:

    猜你喜欢
    • 2023-04-07
    • 1970-01-01
    • 2019-04-11
    • 1970-01-01
    • 2012-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多