空指针与其他指针类型的兼容性

Void pointer compatibility with other pointer types

C 的一个熟悉的特性是 void* 可以赋值给任何指针变量或从任何指针变量赋值。在 N1570 中,C11 的标准文档草案,在 6.3.2.3 Pointers:

中指定

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.

但是6.2.7 Compatible type and composite type

All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.

然而,据我所知,该部分 不是 void* 与其他指针类型兼容。所以:

int x = 5;
int *xp = &x;
void *vp = xp;

预计在传统和 6.3.2.3 中完全有效,但在 6.2.7 中似乎是未定义的行为。

我错过了什么?

关键字:所有声明 ...

int x = 5;
int *xp = &x;
void *vp = xp;

这是三个声明,声明了三个独立的对象:xxpvp

你引用的部分的意思是,如果一个文件说

extern int foo;

另一个文件说

extern double *foo;

行为未定义,因为 foo 已声明两次,类型不同。

Q: Yet, as far as I can see, that section does not say void* is compatible with other pointer types /--/ ...perfectly valid by tradition and 6.3.2.3, but would seem to be undefined behavior by 6.2.7.

6.2.7 没有说太多,但指出进一步阅读 6.7.6.1 指针声明,在那里我们可以找到相关部分:

For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.

所以不,void*int* 不兼容,因为 voidint 不兼容。不过,它们可能 指向 兼容类型,您可以 在它们之间自由转换

您发布的代码很好,因为它只涉及 pointed-at 类型。由于 non-compatible 指针类型而导致的未定义行为的示例宁愿是这样的:

void* v;
int* p = *(int**)&v;

这里把指针本身的地址强行转换成了non-compatible类型。它实际上是 void** 类型,但程序员告诉编译器将其视为 int**,然后读取内容就好像它是 int* 一样。正式地,我们通过与存储在该位置的对象不兼容的类型的 "lvalue access" 读取对象。

但实际上,由于 void* 必须转换 to/from 其他对象指针类型而不丢失信息,因此它几乎可以肯定具有与对象指针相同的大小和表示形式。 C 标准并不保证这一点,因此正式威胁它作为另一种类型是未定义的行为。

这与对象的有效类型strict aliasing的C概念有关。