我在Stack Overflow上已经阅读了很多有关C语言中%p
格式说明符用法的答案,但是似乎没有人解释为什么需要显式转换为void*
除char*
以外的所有类型。
我当然知道,强制转换为void*
或从int i;
printf ("%p", &i);
强制转换的要求与可变参数函数的使用有关(请参阅此answer的第一条评论),否则则不是强制性的。
这是一个例子:
&i
发出有关类型不兼容的警告,并将void*
强制转换为char * m = "Hello";
printf ("%p", m);
(根据标准要求,请再次参见here)。
这部分代码可以顺利编译,而不会抱怨类型转换:
char*
如何从此命令中“解除” -W -Wall -std=c11 -pedantic
?
PS :值得一提的是,我在 x86_64 体系结构上工作,因为指针类型的大小取决于它,并使用 gcc 作为具有.Machine$double.eps
编译选项的linux编译器。
答案 0 :(得分:20)
类型为char*
的参数不需要显式强制转换,因为char *
具有与void *
相同的表示和对齐要求。
引用C11
,第§6.2.5章
指向void的指针应具有与 指向字符类型的指针。 (48) [...]
和脚注48)
相同的表示形式和对齐要求旨在表示与函数的参数,函数的返回值以及并集的成员一样可互换。
答案 1 :(得分:9)
C11标准6.2.5 / 28说:</ p>
指向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);
是未定义的行为。
答案 2 :(得分:4)
之所以要这样做,是因为C标准允许对不同类型的指针使用不同的表示形式,但有两个明显的约束条件:
void
和char
或unsigned char
的指针及其限定版本应具有相同的表示形式。因此,在某些体系结构上,int *
和char *
可能具有不同的表示形式,例如大小不同,并且它们可能以不同的方式传递给vararg函数,从而导致int i = 1; printf("%p", &i);
和{ {1}}表现不同。
但是请注意,Posix标准要求所有指针类型必须具有相同的大小和表示形式。因此,在Posix系统上,int i = 1; printf("%p", (void*)&i);
的行为应符合预期。
答案 3 :(得分:3)
来自C标准#6.2.5p28
指向void的指针应与指向字符的指针具有相同的表示和对齐要求。48)类似地,指向兼容类型的合格或不合格版本的指针应具有相同的表示和对齐方式要求。所有指向结构类型的指针应具有相同的表示和对齐要求。指向联合类型的所有指针应具有相同的表示和对齐要求。指向其他类型的指针不必具有相同的表示或对齐要求。 [强调我的]