指针在C ++中被认为是无符号值吗?

时间:2018-01-24 18:11:07

标签: c++ pointer-arithmetic

签名溢出未定义。无符号溢出定义为模运算。

所以我的问题是,以下定义或未定义:

#include <assert.h>
#include <cstdint>

struct X { int x;/* ... anything ... */ };

X array[3] = { 2, 3, 4 /* or other init values that is compatible with X */};
X* element = array + 1;

std::uintptr_t plus1 = 1;
std::uintptr_t minus1 = 0-plus1;


int main()
{
    printf("%p\n%p\n", element + plus1, array + 2);
    printf("\n");
    printf("%p\n%p\n", element + minus1, array);
    assert(element + plus1 == array + 2);
    assert(element + minus1 == array);
}

虽然我陈述plus1 / minus1,但我的意思是任何+/-值。如果我理解正确,这应该工作。我是对的吗?

3 个答案:

答案 0 :(得分:3)

只要涉及的指针保留在单个数组对象中或刚好超过数组的末尾,指针算法就只能很好地定义。从技术上讲,表达式element + minus1是未定义的行为 - 因为minus1是一个非常大的值,它会超出数组的末尾。

现在在实践中,这可能会起作用,但在技术上仍未定义。

答案 1 :(得分:2)

指针算法在抽象机器中定义。

在抽象机器中,ptr+x仅在对象中存在ptr时才有效,以使其地址在边缘的-x范围内。

这个抽象机器不关心指针或有符号或无符号整数的特定大小。在此抽象机器中,有符号和无符号整数的值为实数整数或未指定的值。

具有32位minus1

uintptr_t等于0xffffffff,一个大的正整数。

element是否在一个足够大的对象中指向0xffffffff * sizeof(X)以后它仍然在对象中?不,它没有。

所以element+minus1是未定义的操作。任何事情都可能发生。

在你的硬件上,指针算法在机器代码中的天真错误可能导致它缠绕。但依靠这一点并不安全。

首先,优化者有时喜欢证明事物。如果他们证明element大于array的地址,那么 element的无符号添加可能使其等于array 。因此,编译器可以将element+unsigned value == array优化为false

如果您更改优化设置,升级编译器或完全无害的内容(如更改内联内容,内联其他代码或链接时优化启发式或月亮更改阶段),则可能会发生此类优化。

依赖它工作是危险的,即使它确实如此,因为你现在负责审核而不是源代码,而是它生成的机器代码。

答案 2 :(得分:1)

  • std::uintptr_t是无符号积分。
  • std::intptr_t是签名的积分。

因此std::uintptr_t的溢出被定义,而std::intptr_t的溢出导致UB。

此外,指针算法仅在数组内有效(到数组末尾之后)。

minus1std::uintptr_t可以容纳的最大数字。

来自http://en.cppreference.com/w/cpp/language/operator_arithmetic

  
      
  • 如果指针P指向数组的第i个元素,则表达式P + n,n + P和Pn是指向i + nth,i + nth和i-的相同类型的指针。分别是同一数组的第n个元素。指针添加的结果也可以是一个过去的结束指针(即指针P,使得表达式P-1指向数组的最后一个元素)。任何其他情况(即,尝试生成指向不指向同一数组的元素或指向结束的指针的指针)会调用未定义的行为。
  •   

element + minus1element - 1不同。
element + minus1超出了有效的数组范围,因此导致UB。