printf("%p")并转换为(void*)

zmb*_*mbq 16 c printf pointers

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

int *my_ptr = ....

printf("My pointer is: %p", (void *)my_ptr);
Run Code Online (Sandbox Code Playgroud)

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

这是当然的,真实的,但是当我已经在的情况下有一个指针,像上面,我为什么要由铸铁int *void *?什么时候int*与void*不同?事实上,什么时候(void *)my_ptr生成任何与简单不同的机器代码my_ptr

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

oua*_*uah 20

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

从C标准:

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

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

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

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

  • 挑剔:你不能“调用”未定义的行为;这是程序所具有的东西,或者更确切地说是没有的东西。程序的行为未定义(即没有明确定义的行为) (3认同)
  • @LightnessRacesinOrbit 嘘,我们都是强大的巫师,拥有罕见的力量,可以通过召唤*未定义行为*的无敌武器来引发不可预测的混乱和绝对的恐怖,以让世界各地的程序员颤抖而闻名! (2认同)
  • @LightnessRacesinOrbit 它可以取决于执行流程,例如“if ( getchar() == 'x' ) 1/0;” 仅当输入当时有“x”时才具有未定义的行为。在我看来,使用“调用 UB”来表示“如果执行到达此语句,则程序的行为未定义”似乎很好。 (2认同)

AnT*_*AnT 17

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

换句话说,通常情况下,不同的指针类型具有不同的表示.int *不同于void *和不同double *.就C语言而言,您的平台使用相同的表示形式void *并且int *只不过是巧合.

一些指针类型都要求有相同的表述语言状态,其中包括void *主场迎战char *,指向不同的结构类型,或者说,int *const int *.但这些只是一般规则的例外.


hac*_*cks 5

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

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


zwo*_*wol 5

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

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

int a;
printf("%p\n", &a);
Run Code Online (Sandbox Code Playgroud)

有未定义的行为.

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

  • 这个解释总是让我不满意——为什么标准不能在调用可变参数函数时强制要求对任何指针类型隐式提升为“void *”,就像它对“float”->“double”和整型函数所做的那样类型?几乎在任何平台上,它都是无操作的,而且我预计即使在上面引用的 Crays 上它也相当便宜(几乎肯定比“float”到“double”转换便宜)。 (2认同)
  • @MatteoItalia 20 世纪 80 年代末的 C 标准化是关于编写***现有***实现。鉴于现有的内存模型[具有可变的指针大小](https://en.wikipedia.org/wiki/Intel_Memory_Model#Pointer_sizes),可能没有共同的基础来标准化可变参数函数中的隐式指针提升,而不会破坏现有的方式代码库。仅仅标准化“void”类型本身可能已经是委员会所能做到的了——K&R C 没有“void”类型。 (2认同)

chu*_*ica 5

\n

当使用 printf 打印指针值时,调用者必须将指针强制转换为 void *

\n
\n

即使强制转换为void *也不足以满足所有指针的需要。

\n

C 有两种指针:对象指针和函数指针。

\n

任何对象指针void*都可以毫无问题地转换为:

\n
printf("My pointer is: %p", (void *)my_ptr); // OK when my_ptr points to an object\n
Run Code Online (Sandbox Code Playgroud)\n

函数指针的转换void *未完全定义。

\n

考虑 2021 年的系统,其中void *64 位,函数指针为 128 位。

\n
\n

C 确实指定了(我的重点)

\n
\n

任何指针类型都可以转换为整数类型。除非前面指定,否则结果是实现定义的。如果结果不能以整数类型表示,则行为未定义。结果不必在任何整数类型的值范围内。C17dr \xc2\xa7 6.3.2.3 6

\n
\n

要打印函数指针可以尝试:

\n
printf("My function pointer is: %jX", (uintmax_t) my_function_ptr); // Selectively OK\n
Run Code Online (Sandbox Code Playgroud)\n
\n

C 缺乏真正的通用指针,也缺乏打印函数指针的干净方法。

\n