计算地址差异是未定义的行为吗?

Posted

技术标签:

【中文标题】计算地址差异是未定义的行为吗?【英文标题】:Is calculating address difference undefined behaviour? 【发布时间】:2020-08-28 15:06:34 【问题描述】:

假设我执行以下操作:

void g(int* x)

    int y = 0;
    auto diff = uintptr_t(&y) - uintptr_t(x);


void f()

    int x = 0;
    g(&x);

diff 是否仅具有未定义的值,或者代码是否调用未定义的行为?根据规范,代码是否保证运行良好并计算diff 的值,可能毫无意义,或者它是否调用UB?我相信有一些关于不相关变量的东西,但无法确定它。

我对自(包括)C++ 11 以来的任何标准的答案感兴趣。

来自 cmets 的讨论:Print stack in C++

【问题讨论】:

见:timsong-cpp.github.io/cppwp/expr.add#5 @NathanOliver - 这适用于指针本身,而不适用于从它们获得的整数。 @JesperJuhl - 代码没有减去 指针 std::ptrdiff_t 定义了一个类型 @QuentinUK 这在这里没用。如果不转换为uintptr_t,由于不受单个对象/数组的限制,您的减法将具有未定义的行为。事实上,这就是使用 uintptr_t 的全部原因。 【参考方案1】:

引用 C++11 标准草案。关于将指针转换为整数的主题

[expr.reinterpret.cast]

5整数类型或枚举类型的值可以显式 转换为指针。转换为整数的指针 足够的大小(如果实现中存在这样的大小)并返回到 相同的指针类型将具有其原始值;之间的映射 指针和整数是由实现定义的。

由于必须为要编译的代码定义uintptr_t,因此目标机器上存在一个整数类型,可以作为指针到整数转换的目标。映射是实现定义的,但最重要的是结果不是不确定的。这意味着您为这两种转换都获得了一些有效的整数。

所以减法不是未定义的行为。但结果是实现定义的。

【讨论】:

这非常有用。我没有意识到 uintptr_t 的演员阵容有多么重要。如果代码直接&y - x,答案仍然成立吗? @Jeffrey - 不。直接减去指针是未定义的。 Nathan Oliver 在您的帖子下的评论有相关的标准段落。【参考方案2】:

将指针转换为足够大小的整数是明确定义的,从另一个整数中减去无符号整数是明确定义的,无论它们的值如何。这里没有未定义的行为。

此外,标准不保证转换后的整数有任何特定值,因此也不保证它们的减法结果。

【讨论】:

以上是关于计算地址差异是未定义的行为吗?的主要内容,如果未能解决你的问题,请参考以下文章

C 中的 sum+++i 是未定义的行为吗? [复制]

i += ++i 在 C++0x 中是未定义的行为吗?

如果 a 未初始化,a^a 或 a-a 是未定义的行为吗?

不调用对象的析构器是未定义的行为吗?

在 C++ 中删除空指针是不是被认为是未定义的行为? [复制]

为啥在已删除指针上调用非虚拟成员函数是未定义的行为?