代码之家  ›  专栏  ›  技术社区  ›  zmbq

printf(“%p”)并铸造到(void*)

  •  26
  • zmbq  · 技术社区  · 10 年前

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

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

    就我而言,我不知道为什么。我找到了 this question ,这几乎相同。问题的答案是正确的——它解释了int和指针不一定是相同的长度。

    当然,这是真的,但当我 已经 有一个指针,就像上面的例子,我为什么要从 int * void * ? 什么时候int*与void*不同?事实上,什么时候 (void *)my_ptr 生成不同于简单 my_ptr ?

    更新: 多个知识渊博的响应者引用了该标准,称通过错误的类型可能会导致不明确的行为。怎样我想 printf("%p", (int *)ptr) printf("%p", (void *)ptr) 以生成完全相同的堆栈帧。两个调用何时生成不同的堆栈帧?

    7 回复  |  直到 5 年前
        1
  •  32
  •   RobertS supports Monica Cellio    5 年前

    这个 %p 转换说明符需要类型为的参数 void * 。如果不传递类型为的参数 无效* ,函数调用调用未定义的行为。

    根据C标准(C11,7.21.6.1p8格式化输入/输出功能):

    “p-参数应为指向void的指针。”

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

    具有不同指针类型表示的实现的一个示例是Cray PVP,其中指针类型表示为64位 无效* char * 但对于其他指针类型为32位。

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

        2
  •  20
  •   AnT stands with Russia    8 年前

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

    换句话说,在一般情况下,不同的指针类型具有不同的表示。 整数* 不同于 无效* 和不同于 double * 。事实上,您的平台对 无效* 整数* 就C语言而言,这不过是巧合。

    该语言指出,某些指针类型需要具有相同的表示,包括 无效* 与。 char * ,指向不同结构类型的指针, 整数* const int * 。但这些只是一般规则的例外。

        3
  •  10
  •   zwol    10 年前

    其他人已经充分解决了通过 int * 具有固定数量参数的原型函数 需要不同的指针类型。

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

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

    具有未定义的行为。

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

        4
  •  5
  •   Community CDub    4 年前

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

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

        5
  •  3
  •   R.. GitHub STOP HELPING ICE    10 年前

    实际上,除了在古代的大型机/小型机上,不同的指针类型极不可能有不同的 尺寸 。然而,它们有不同之处 类型 ,并符合规范 printf ,使用错误的格式说明符类型参数调用它会导致 未定义的行为 。这意味着不要这样做。

        6
  •  3
  •   chux    4 年前

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

    均匀铸造至 void * 不足以用于所有指针。

    C有两种指针:指向 物体 和指向 功能 .

    任何 对象指针 可以转换为 void* 没有问题:

    printf("My pointer is: %p", (void *)my_ptr); // OK when my_ptr points to an object
    

    转换为 指向函数的指针 无效* 未定义。

    考虑2021的系统,其中 无效* 是64位,函数指针是128位。


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

    任何 指针类型可以转换为整数类型。除非前面指定,否则结果是由实现定义的。如果结果不能以整数类型表示,则行为未定义。结果不需要在任何整数类型的值范围内。第17条§6.3.2.3 6

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

    printf("My function pointer is: %ju", (uintmax_t) my_function_ptr); // Selectively OK
    

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

        7
  •  1
  •   M.M    4 年前

    解决问题:

    两个调用何时生成不同的堆栈帧?

    编译器可能会注意到行为未定义,并发出异常、非法指令等。编译器不需要尝试生成堆栈帧、函数调用等。

    See here 例如编译器在UB的另一种情况下这样做。它不生成带有空参数的差异指令,而是生成 ud2 非法指令。

    当根据语言标准未定义行为时,对编译器的行为没有要求。