可以使用std :: uintptr_t来避免越界指针算法的未定义行为吗?

时间:2014-03-25 02:36:44

标签: c++ pointers pointer-arithmetic

现在我们知道,越界 - 指针算术具有未定义的行为,如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<char*>(reinterpret_cast<std::uintptr_t>(p) + n)是否会保证产生相同的有效指针?

2 个答案:

答案 0 :(得分:12)

不,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位字节;递增通过转换uintptr_t获得的char*会使其前进到指向下一个64位字。

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

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

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

答案 1 :(得分:4)

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

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

  

5.2.10重新解释投射

     

4。指针可以显式转换为足以容纳它的任何整数类型。映射功能是   实施德音响定义

     

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

(请注意,通常情况下,编译器无法知道您减去了一个,然后将其添加回来。)

相关问题