在最近的一个问题中,有人提到当使用printf打印指针值时,调用者必须将指针强制转换为void *,如下所示:
int *my_ptr = ....
printf("My pointer is: %p", (void *)my_ptr);
对于我的生活,我无法弄清楚为什么 . 我找到this question,几乎是一样的 . 问题的答案是正确的 - 它解释了整数和指针的长度不一定相同 .
当然,这是真的,但是当我已经有一个指针时,就像上面的情况一样,我为什么要从 int *
转换为 void *
?什么时候int *与void *不同?事实上, (void *)my_ptr
什么时候生成的任何机器代码都与 my_ptr
不同?
更新:多个知识渊博的响应者引用了标准,说传递错误的类型可能会导致未定义的行为 . 怎么样?我希望 printf("%p", (int *)ptr)
和 printf("%p", (void *)ptr)
生成完全相同的堆栈帧 . 两个调用何时生成不同的堆栈帧?
5 回答
在C语言中,所有指针类型的表示可能不同 . 所以,是的,
int *
与void *
不同 . 可以很难(或不可能)找到能够说明这种差异的真实平台,但在概念层面,差异仍然存在 .换句话说,通常情况下,不同的指针类型具有不同的表示 .
int *
与void *
不同,与double *
不同 . 对于void *
和int *
,您的平台使用相同的表示这一事实只不过是巧合,就C语言而言 .该语言指出某些指针类型需要具有相同的表示形式,其中包括
void *
与char *
,指向不同结构类型的指针,或者说int *
和const int *
. 但这些只是一般规则的例外 .p
转换说明符需要void *
类型的参数 . 如果未传递void *
类型的参数,则函数调用将调用未定义的行为 .从C标准:
C中的指针类型不需要具有相同的大小或相同的表示 .
具有不同指针类型表示的实现的示例是Cray PVP,其中指针类型的表示对于
void *
和char *
是64位而对于其他指针类型是32位 .见"9.1.2.2",表3中的"Cray C/C++ Reference Manual",http://docs.cray.com/books/004-2179-003/004-2179-003-manual.pdf
其他人已经充分解决了将
int *
传递给需要不同指针类型的 prototyped function with a fixed number of arguments 的情况 .printf
不是这样的功能 . 它是一个可变参数函数,因此默认参数提升用于其匿名参数(即格式字符串后面的所有内容),并且如果每个参数的提升类型与格式效应器所期望的类型不完全匹配,则行为未定义 . 特别是, even ifint *
和void *
具有相同的表示,有未定义的行为 .
这是因为调用帧的布局可能取决于每个参数的确切具体类型 . 为指针和非指针类型指定不同参数区域的ABI已在现实生活中发生(例如,Motorola 68000希望您尽可能地在地址寄存器和数据寄存器中的非指针中保持指针) . 我不知道任何真实的ABI会分离不同的指针类型,但它是允许的,听到一个我也不会感到惊讶 .
实际上除了古代大型机/迷你之外,不同的指针类型极不可能具有不同的大小 . 但是它们具有不同的类型,并且根据
printf
的规范,使用格式说明符的错误类型参数调用它会导致未定义的行为 . 这意味着不要这样做 .c11:7.21.6格式化输入/输出功能(p8):