printf(“%p”)并转换为(void *)

在最近的一个问题中,有人提到当使用printf打印指针值时,调用者必须将指针强制转换为void *,如下所示:

int *my_ptr = .... printf("My pointer is: %p", (void *)my_ptr); 

对于我的生活,我无法弄清楚为什么。 我发现了这个问题 ,几乎是一样的。 问题的答案是正确的 – 它解释了整数和指针的长度不一定相同。

当然,这是正确的,但是当我已经有一个指针时,就像上面的情况一样,我为什么要从int *void * ? 什么时候int *与void *不同? 事实上,什么时候(void *)my_ptr生成的任何机器代码都与my_ptr不同?

更新:多个知识渊博的响应者引用了标准,说传递错误的类型可能会导致未定义的行为。 怎么样? 我希望printf("%p", (int *)ptr)printf("%p", (void *)ptr)生成完全相同的堆栈帧。 两个调用何时生成不同的堆栈帧?

在C语言中,所有指针类型的表示可能都不同。 所以,是的, int *void *不同。 可以很难(或不可能)找到能够说明这种差异的真实平台,但从概念上讲,差异仍然存在。

换句话说,通常情况下,不同的指针类型具有不同的表示。 int *void *不同,与double *不同。 就C语言而言,您的平台对void *int *使用相同的表示这一事实只不过是巧合。

该语言指出某些指针类型需要具有相同的表示,其中包括void *char * ,指向不同结构类型的指针,或者说, int *const int * 。 但这些只是一般规则的例外。

p转换说明符需要void *类型的参数。 如果未传递void *类型的参数,则函数调用将调用未定义的行为。

从C标准:

(C11,7.21.6.1p8格式化输入/输出函数)“p参数应为指向void的指针。”

C中的指针类型不需要具有相同的大小或相同的表示。

具有不同指针类型表示的实现的示例是Cray PVP,其中指针类型的表示对于void *char *是64位而对于其他指针类型是32位。

请参阅“Cray C / C ++参考手册”,表3中的“9.1.2.2” http://docs.cray.com/books/004-2179-003/004-2179-003-manual.pdf

其他人已经充分解决了将int *传递给原型函数的情况,该函数具有固定数量的参数 ,这些参数需要不同的指针类型。

printf不是这样的function。 它是一个可变参数函数,因此默认参数升级用于其匿名参数(即格式字符串之后的所有内容),并且如果每个参数的提升类型与格式效应器所期望的类型不完全匹配,则行为未定义。 特别是, 即使 int *void *具有相同的表示,

 int a; printf("%p\n", &a); 

有未定义的行为。

这是因为调用帧布局可能取决于每个参数的确切具体类型。 在指定和非指针类型中指定不同参数区域的ABI已在现实生活中发生(例如,Motorola 68000希望您尽可能在地址寄存器和数据寄存器中的非指针中保留指针)。 我不知道任何真实的ABI会分离不同的指针类型,但它是允许的,听到一个我也不会感到惊讶。

实际上除了古代大型机/迷你之外,不同的指针类型极不可能具有不同的大小 。 但是它们具有不同的类型 ,并且根据printf的规范,使用格式说明符的错误类型参数调用它会导致未定义的行为 。 这意味着不要这样做。

c11:7.21.6格式化输入/输出function(p8):

p参数应该是指向void指针 。 指针的值以实现定义的方式转换为打印字符序列。