%p 格式说明符需要对所有类型显式转换为 void*,但 printf 中的 char* 除外

%p format specifier needs explicit cast to void* for all types but char* in printf

我在 Stack Overflow 中阅读了很多关于 C 语言中 %p 格式说明符用法的答案,但是 none 似乎给出了为什么显式转换为 [=所有类型都需要 13=] 但 char*.
我当然知道这样一个事实,即向 void* 或从 void* 强制转换的要求与可变参数函数的使用相关(请参阅此 answer 的第一条评论),而在其他情况下则不是强制性的。

这是一个例子:

int i;    
printf ("%p", &i);

产生关于类型不兼容的警告,&i 应转换为 void*(根据标准要求,再次参见 here)。

虽然这段代码编译顺利,没有任何关于类型转换的抱怨:

char * m = "Hello";    
printf ("%p", m);

char* 是"relieved" 这个祈使句怎么来的?

PS:也许值得补充的是我在 x86_64 体系结构,因为指针类型的大小取决于它,并使用 gcc 作为 linux 上的编译器和 -W -Wall -std=c11 -pedantic 编译选项。

char* 类型的参数不需要显式转换,因为 char *void * 具有相同的表示和对齐要求。

引用 C11,章节 §6.2.5

A pointer to void shall have the same representation and alignment requirements as a pointer to a character type. (48) [...]

和脚注 48)

The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and members of unions.

来自 C 标准#6.2.5p28

A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.48) Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements. [emphasis mine]

C11 标准 6.2.5/28 说:

A pointer to void shall have the same representation and alignment requirements as a pointer to a character type. 48)

脚注 48 为:

The same representation and alignment requirements are meant to imply interchangeability as arguments to functions, return values from functions, and members of unions.

然而 7.21.6.1 ("The fprintf function") 说 %p:

The argument shall be a pointer to void.


这显然是矛盾的。在我看来,一个明智的解释是 6.2.5/28 的意图是 void *char * 实际上可以互换,因为函数参数的类型不对应于原型。 (即非原型函数的参数,或匹配可变参数函数原型的省略号)。

显然您正在使用的编译器采用类似的观点。

为了支持这一点,7.21.6.1 中参数类型的规范,如果从字面上理解而不考虑意图,在实践中有很多其他的不一致之处必须被忽略(例如它说 printf("%lx", -1); 是明确定义的,但 printf("%u", 1); 是未定义的行为)。

此要求的原因是 C 标准允许对指向不同类型的指针进行不同的表示,但有 2 个显着限制:

  • 指向voidcharunsigned char的指针及其限定版本应具有相同的表示形式。
  • pointers to structures and unions must have the same representation.

因此在某些架构上,int *char * 可能有不同的表示,例如不同的大小,并且它们可以以不同的方式传递给 vararg 函数,导致 int i = 1; printf("%p", &i);int i = 1; printf("%p", (void*)&i); 表现不同。

但是请注意,Posix 标准要求所有指针类型具有相同的大小和表示形式。因此在 Posix 系统上 printf("%p", &i); 应该按预期运行。