在最近的一个问题中,有人提到当使用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)
生成完全相同的堆栈帧。两个调用何时生成不同的堆栈帧?
答案 0 :(得分:19)
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
答案 1 :(得分:15)
在C语言中,所有指针类型的表示形式可能不同。所以,是的,int *
与void *
不同。可以很难(或不可能)找到能够说明这种差异的真实平台,但在概念层面,差异仍然存在。
换句话说,通常情况下,不同的指针类型具有不同的表示。 int *
与void *
不同,与double *
不同。就C语言而言,您的平台对void *
和int *
使用相同的表示这一事实只不过是巧合。
该语言指出某些指针类型需要具有相同的表示形式,包括void *
与char *
,指向不同结构类型的指针,或者说,int *
和{{1 }}。但这些只是一般规则的例外。
答案 2 :(得分:5)
其他人已经充分解决了将int *
传递给具有固定数量参数的原型函数的情况,该函数需要不同的指针类型。
printf
不是这样的功能。它是一个可变函数,因此默认参数提升用于其匿名参数(即格式字符串后面的所有内容),并且如果每个参数的提升类型不是完全匹配格式效应器所期望的类型,行为未定义。特别是,即使 int *
和void *
具有相同的代表性,
int a;
printf("%p\n", &a);
有未定义的行为。
这是因为调用框架的布局可能取决于每个参数的确切具体类型。为指针和非指针类型指定不同参数区域的ABI已在现实生活中发生(例如,Motorola 68000希望您尽可能地在地址寄存器和数据寄存器中的非指针中保持指针)。我不知道任何真实的ABI会隔离不同的指针类型,但它是允许的,听到一个就不会让我感到惊讶。
答案 3 :(得分:3)
实际上除了古代大型机/迷你之外,不同的指针类型极不可能具有不同的大小。但是它们具有不同的类型,并且根据printf
的规范,使用格式说明符的错误类型参数调用它会导致未定义的行为。这意味着不要这样做。
答案 4 :(得分:3)
p
参数应为指向void
的指针。指针的值是 转换为一系列打印字符,在实现定义中 方式。
答案 5 :(得分:1)
解决问题:
<块引用>这两个调用什么时候会生成不同的栈帧?
编译器可能会注意到行为未定义,并发出异常、非法指令等。编译器不需要尝试生成堆栈帧、函数调用或其他任何东西。
See here 是编译器在 UB 的另一种情况下执行此操作的示例。它不是生成带有空参数的尊重指令,而是生成 ud2
非法指令。
当根据语言标准未定义行为时,对编译器的行为没有要求。
答案 6 :(得分:1)
当用printf打印指针值时,调用者必须将指针强制转换为void *
对于所有指针来说,即使转换为 void *
也是不够的。
C 有两种指针:指向对象的指针和指向函数的指针。
任何对象指针都可以毫无问题地转换为void*
:
printf("My pointer is: %p", (void *)my_ptr); // OK when my_ptr points to an object
未定义函数指针到void *
的转换。
考虑 2021 年的系统,其中 void *
为 64 位,函数指针为 128 位。
C 确实指定了(我的重点)
<块引用>任何指针类型都可以转换为整数类型。除了前面指定的之外,结果是实现定义的。如果结果不能以整数类型表示,则行为未定义。结果不必在任何整数类型的值范围内。 C17dr § 6.3.2.3 6
要打印函数指针可以尝试:
printf("My function pointer is: %ju", (uintmax_t) my_function_ptr); // Selectively OK
C 缺乏真正通用的指针,也缺乏打印函数指针的干净方法。