指针整数转换的定义

Definedness of pointer-integer casts

我对从指针到整数和各种相关操作的强制转换的定义性(未定义性、实现定义性)感兴趣。我主要对 C11 感兴趣,但欢迎回答其他标准版本(甚至 C++)。

为了这个问题的目的,假设 C 实现提供 intptr_t

考虑以下函数:

#include <assert.h>
#include <stdint.h>

int x;
int y;
int z[2];

void f1(void) {
    int *p = &x;
    intptr_t i = p;
}

void f2(void) {
    int *p = &x;
    intptr_t i1 = p;
    intptr_t i2 = p;
    assert(i1 == i2);
}

void f3(void) {
    int *p1 = &x;
    int *p2 = &y;
    intptr_t i1 = p1;
    intptr_t i2 = p2;
    assert(i1 != i2);
}

void f4(void) {
    int *p1 = &x;
    intptr_t i1 = p1;
    int *p2 = i1;
    intptr_t i2 = p2;
    assert(i1 == i2);
}

void f5(void) {
    int *p1 = &z[0];
    int *p2 = &z[1];
    intptr_t i1 = p1;
    intptr_t i2 = p2;
    assert(i1 < i2);
}

这是 C11 标准对 intptr_t 的描述:

7.20.1.4 Integer types capable of holding object pointers

The following type designates a signed integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:

intptr_t

uintptr_t 也是如此(除了有符号 -> 无符号)。

同样来自“6.5.4p3 Cast operators”:

Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast.

其中 6.5.16.1 没有提到将指针分配给整数类型,反之亦然(0 常量除外)。这意味着你在分配时确实需要一个强制转换,gcc 只允许它作为编译器扩展(而且它根本不编译 -pedantic-errors

至于在这些转换中返回的确切值,标准是这样说的:

6.3.2.3 Pointers

p5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, [...]

p6 Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. [...]


您拥有的基本保障是:

int x;
(int*) (void*) (intptr_t) (void*) &x == &x;
/* But the void* casts can be implicit */
(int*) (intptr_t) &x == &x;

而且不必强制转换为相同的整数。例如,以下可能为真:

int x;
(intptr_t) &x != (intptr_t) &x;

在必要时添加强制转换,并将您的断言转换为 returns(因为 assert(false) 是未定义的行为),none 您的函数具有未定义的行为,但是 f2 , f4f5 可以为假。 f3 必须为真,因为两个整数必须不同才能转换为不同的指针。

如果intptr_t存在,那么它能够在不丢失数据的情况下保存void*

The following type designates a signed integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:

   intptr_t

(§7.20.1.4p1)

但是,如果指针不是指向 void 的指针,则所有赌注均无效,除非它是空指针:

An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation. Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type. (§6.3.2.3p5-6)

“前面说明”是void*和整型之间的转换,空指针常量到整型的转换

所以严格正确的程序需要插入 void* 强制转换:

intptr_t i = (intptr_t)(void*)p;
T* p = (void*)i;

没关系,因为任何对象类型和void*之间的往返转换保证是无损的:

A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

(第一行中显式 intptr_t 的情况是必要的,因为赋值不会从指针隐式转换为整数,尽管一些编译器允许将其作为扩展。但是,在 C 中,赋值操作会隐式转换在 void* 和其他指针类型之间(§6.5.16.1p1,第四个要点)。)

请注意,“对象类型”不包括函数指针。

此外,序列 void*intptr_tvoid* 的结果等于原始值并不意味着序列 intptr_tvoid*intptr_t有相同的属性。虽然指针和整数之间的转换“旨在与执行环境的寻址结构保持一致”,但该声明位于脚注中,因此并不规范。无论如何,“执行环境的寻址结构”有可能允许同一地址的多种表示。 (您不必看得太远就能找到示例 :-)。 )