【问题标题】:Can std::uintptr_t be used to avoid undefined behavior of out-of-bounds pointer arithmetic?可以使用 std::uintptr_t 来避免越界指针算术的未定义行为吗?
【发布时间】:2014-03-25 02:36:43
【问题描述】:

现在我们知道,越界指针算术具有未定义的行为,如 SO question 中所述。

我的问题是:我们可以通过转换为 std::uintptr_t 进行算术运算然后转换回指针来解决这种限制吗?可以保证有效吗?

例如:

char a[5];
auto u = reinterpret_cast<std::uintptr_t>(a) - 1;
auto p = reinterpret_cast<char*>(u + 1); // OK?

现实世界的用途是优化偏移内存访问——而不是p[n + offset],我想做offset_p[n]

编辑使问题更明确:

给定一个 char 数组的基指针 p,如果 p + n 是一个有效指针,是否保证 reinterpret_cast&lt;char*&gt;(reinterpret_cast&lt;std::uintptr_t&gt;(p) + n) 产生相同的有效指针?

【问题讨论】:

  • 也许应该注意的是,在重要的平台上,现代编译器会为您优化,而在大多数其他平台上,可以在计算有效地址时添加一个小的偏移量,因此没有开销。

标签: c++ pointers pointer-arithmetic


【解决方案1】:

不,uintptr_t 不能有意义地用于避免在执行指针运算时出现未定义的行为。

一方面,至少在 C 中不能保证 uintptr_t 甚至存在。要求是void* 类型的任何值都可以转换为uintptr_t 并再次返回,从而在不丢失信息的情况下产生原始值。原则上,可能没有足够宽的无符号整数类型来容纳所有指针值。 (我认为这同样适用于 C++,因为 C++ 继承了大部分 C 标准库并通过引用 C 标准来定义它。)

即使uintptr_t 确实存在,也不能保证对uintptr_t 值的给定算术运算与对指针值的相应运算执行相同的操作。

例如,我研究过在软件中实现字节指针的系统(Cray 矢量系统、T90 和 SV1)。本机地址是指 64 位字的 64 位地址;字节寻址没有硬件支持。 char*void* 指针由一个字指针组成,其 3 位偏移存储在其他未使用的高位中。整数和指针之间的转换只是简单地复制位。因此,增加 char* 会使其指向内存中的下一个 8 位字节;递增通过转换 char* 获得的 uintptr_t 将使其指向下一个 64 位字。

这只是一个例子。更一般地说,指针和整数之间的转换是实现定义的,语言标准不保证这些转换的语义(除了在某些情况下转换回指针)。

所以是的,您可以将指针值转换为 uintptr_t(如果存在该类型)并对其执行算术运算,而不会冒未定义行为的风险——但结果可能有意义,也可能没有意义。

碰巧,在大多数系统上,指针和整数之间的映射更简单,您可能可以摆脱这种游戏。但是你最好直接使用指针算法,并且小心避免任何无效操作。

【讨论】:

    【解决方案2】:

    是的,这是合法的,但您必须reinterpret_cast 的值与char* 完全相同。

    (因此,您打算做的事情是非法的;也就是说,将 不同的值转换回指针。)

    5.2.10 重新解释演员表

    4 。指针可以显式转换为任何大到足以容纳它的整数类型。映射函数为 实现定义。

    5 .整数类型或枚举类型的值可以显式转换为指针。指针转换 到一个足够大小的整数(如果实现中存在这样的整数)并返回到相同的指针类型 将有其原始价值;

    (请注意,一般来说,编译器不可能知道你减去了一个然后又把它加回来。)

    【讨论】:

    • 所以reinterpret_cast&lt;pointer&gt;(offset_p - offset + n_less_equal_array_size) 总是表现良好,其中 offset_p 将偏移 ptr 值存储在 std::uintptr_t 中?
    • 不确定我是否关注。但一般的答案是,如果您传递给reinterpret_cast 的整数与您从中得到的值相同,那么它是合法的。如果不是,那就是非法的。
    • 对于数组的第一个元素,它是相同的值,但我想知道它是否也适用于数组中的其他元素。
    • 不,这是非法的。规范说“映射函数是实现定义的”,所以不能保证f(p) + 1 = f(p + 1).
    • 谢谢,这清楚地回答了我的疑问。所以标题问题的答案应该是NO。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-30
    • 1970-01-01
    • 1970-01-01
    • 2013-10-19
    • 2013-11-12
    相关资源
    最近更新 更多