指针在 C++ 中是否被视为无符号值?
Is a pointer considered an unsigned value in C++?
有符号溢出未定义。无符号溢出定义为模运算。
所以我的问题是,以下是已定义还是未定义:
#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
,但我的意思是任何 +/- 值。如果我理解正确,这应该有效。我说得对吗?
std::uintptr_t
是无符号整数。
std::intptr_t
是有符号积分。
因此 std::uintptr_t
的溢出被定义,而 std::intptr_t
的溢出导致 UB。
此外,指针运算仅在数组内有效(到数组末尾的一位)。
minus1
是std::uintptr_t
能容纳的最大数。
来自http://en.cppreference.com/w/cpp/language/operator_arithmetic
- If the pointer P points to the ith element of an array, then the expressions P+n, n+P, and P-n are pointers of the same type that point to the i+nth, i+nth, and i-nth element of the same array, respectively. The result of pointer addition may also be a one-past-the-end pointer (that is, pointer P such that the expression P-1 points to the last element of the array). Any other situations (that is, attempts to generate a pointer that isn't pointing at an element of the same array or one past the end) invoke undefined behavior.
element + minus1
与 element - 1
不同。
element + minus1
超出数组有效范围,导致UB。
只有当所涉及的指针保留在单个数组对象内或刚好超过数组末尾时,指针算法才被明确定义。所以从技术上讲,表达式 element + minus1
是未定义的行为——因为 minus1
是一个非常大的值,它超出了数组的末尾。
现在在实践中,这可能会奏效,但在技术上仍未定义。
抽象机中定义了指针算法。
在抽象机中,ptr+x
仅当 ptr
存在于对象中且其地址在边的 -x
内时才有效。
这个抽象机不关心指针或有符号或无符号整数的具体大小。在此抽象机中,有符号和无符号整数的值是 实整数 或未指定的值。
minus1
加上一个32位uintptr_t
等于0xffffffff,一个大的正整数。
element
是否指向一个足够大的对象,以至于 0xffffffff*sizeof(X) 之后它仍然在该对象内?不,它没有。
所以element+minus1
是一个未定义的操作。任何事情都可能发生。
在您的硬件上,将指针算术直接解释为机器代码可能导致它环绕。但是依赖这个并不安全。
一方面,优化器有时喜欢证明事情。如果他们证明 element
大于 array
的地址,那么 对 element
的无符号加法可能会使它等于 array
.所以编译器可以优化 element+unsigned value == array
到 false
.
如果您更改优化设置、升级编译器或完全无害的事情(例如更改内联位置或其他内联代码)或 link 时间优化时的启发式方法,或月相变化。
依靠它工作是危险的,即使它确实如此,因为您现在负责审核的不是源代码,而是它生成的机器代码。
有符号溢出未定义。无符号溢出定义为模运算。
所以我的问题是,以下是已定义还是未定义:
#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
,但我的意思是任何 +/- 值。如果我理解正确,这应该有效。我说得对吗?
std::uintptr_t
是无符号整数。std::intptr_t
是有符号积分。
因此 std::uintptr_t
的溢出被定义,而 std::intptr_t
的溢出导致 UB。
此外,指针运算仅在数组内有效(到数组末尾的一位)。
minus1
是std::uintptr_t
能容纳的最大数。
来自http://en.cppreference.com/w/cpp/language/operator_arithmetic
- If the pointer P points to the ith element of an array, then the expressions P+n, n+P, and P-n are pointers of the same type that point to the i+nth, i+nth, and i-nth element of the same array, respectively. The result of pointer addition may also be a one-past-the-end pointer (that is, pointer P such that the expression P-1 points to the last element of the array). Any other situations (that is, attempts to generate a pointer that isn't pointing at an element of the same array or one past the end) invoke undefined behavior.
element + minus1
与 element - 1
不同。
element + minus1
超出数组有效范围,导致UB。
只有当所涉及的指针保留在单个数组对象内或刚好超过数组末尾时,指针算法才被明确定义。所以从技术上讲,表达式 element + minus1
是未定义的行为——因为 minus1
是一个非常大的值,它超出了数组的末尾。
现在在实践中,这可能会奏效,但在技术上仍未定义。
抽象机中定义了指针算法。
在抽象机中,ptr+x
仅当 ptr
存在于对象中且其地址在边的 -x
内时才有效。
这个抽象机不关心指针或有符号或无符号整数的具体大小。在此抽象机中,有符号和无符号整数的值是 实整数 或未指定的值。
minus1
加上一个32位uintptr_t
等于0xffffffff,一个大的正整数。
element
是否指向一个足够大的对象,以至于 0xffffffff*sizeof(X) 之后它仍然在该对象内?不,它没有。
所以element+minus1
是一个未定义的操作。任何事情都可能发生。
在您的硬件上,将指针算术直接解释为机器代码可能导致它环绕。但是依赖这个并不安全。
一方面,优化器有时喜欢证明事情。如果他们证明 element
大于 array
的地址,那么 对 element
的无符号加法可能会使它等于 array
.所以编译器可以优化 element+unsigned value == array
到 false
.
如果您更改优化设置、升级编译器或完全无害的事情(例如更改内联位置或其他内联代码)或 link 时间优化时的启发式方法,或月相变化。
依靠它工作是危险的,即使它确实如此,因为您现在负责审核的不是源代码,而是它生成的机器代码。