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

pro*_*rsn 17 c printf types casting void-pointers

%p在Stack Overflow中已经阅读了很多关于C语言中格式说明符用法的答案,但似乎没有一个解释为什么void*所有类型都需要显式转换为char*.
我当然知道这样一个事实,即强制转换的要求void*与使用可变函数(参见本答案的第一条评论)有关,而非强制性要求.

这是一个例子:

int i;    
printf ("%p", &i);
Run Code Online (Sandbox Code Playgroud)

产生关于类型不兼容性的警告,并且&i应该进行铸造void*(根据标准的要求,再次参见此处).

虽然这一大块代码能够顺利编译,但没有关于类型转换的任何投诉:

char * m = "Hello";    
printf ("%p", m);
Run Code Online (Sandbox Code Playgroud)

如何char*从这一要求中"解脱"?

PS:可能值得补充的是我在 x86_64架构上工作,因为指针类型大小依赖于它,并且使用 gcc作为linux上的-W -Wall -std=c11 -pedantic编译器和编译选项.

Sou*_*osh 20

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

引用C11,第6.2.5章

指向void的指针应具有与指向字符类型的指针相同的表示和对齐要求.(48) [...]

和脚注48)

相同的表示和对齐要求意味着可互换性作为函数的参数,函数的返回值和联合的成员.

  • @programmersn它不只是关于显式演员.内存中指针的表示也是相同的. (3认同)

M.M*_*M.M 9

C11标准6.2.5/28说:

指向void的指针应具有与指向字符类型的指针相同的表示和对齐要求.48)

脚注48是:

相同的表示和对齐要求意味着可互换性作为函数的参数,函数的返回值和联合的成员.

但7.21.6.1("fprintf函数")说%p:

参数应该是指向void的指针.


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

显然,您正在使用的编译器采用类似的视图.

为了支持这一点,7.21.6.1中的参数类型的规范,如果按照字面意思而不考虑意图,在实践中有很多其他的不一致性(例如,它说printf("%lx", -1);明确定义,但是printf("%u", 1);未定义的行为) ).


chq*_*lie 5

提出此要求的原因是 C 标准允许对不同类型的指针使用不同的表示形式,但有 2 个值得注意的约束:

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

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