使用%p转换说明符打印空指针是否为未定义的行为?

#include <stdio.h>

int main(void) {
    void *p = NULL;

    printf("%p", p);

    return 0;
}

这个问题适用于C标准,而不是C实现.

推荐答案

This is one of those weird corner cases where we're subject to the limitations of the English language and inconsistent structure in the standard. So at best, I can make a compelling counter-argument, as it's impossible to 100 it :)1


问题中的代码表现出明确的行为.

[7.1.4]是问题的基础,让我们从这里开始:

除非在下面的详细描述中另有明确说明,否则以下语句均适用:如果函数的参数具有无效值(such as a值位于函数域之外,或指针位于程序的地址空间之外,or a null pointer[... other examples ...])[...],则行为未定义.[... other statements ...]

这是一种笨拙的语言.一种解释是,列表中的项目是所有库函数的UB,除非被单独的描述覆盖.但是这个列表以"比如"开头,表明它是说明性的,而不是详尽的.例如,它没有提到字符串的正确空终止(对于例如strcpy的行为至关重要).

因此,很明显,7.1.4的意图/范围只是"无效值"导致UB(unless stated otherwise).我们必须查看每个函数的描述,以确定什么是"无效值".

Example 1 - strcpy

[7.21.2.3]只说:

strcpy函数将s2指向的字符串(包括终止空字符)复制到s1指向的数组中.如果在重叠的对象之间进行复制,则行为未定义.

它没有明确提到空指针,也没有提到空终止符.取而代之的是,从"s2指向的字符串"推断,唯一有效的值是字符串(即指向以NULL结尾的字符数组的指针).

事实上,这种模式可以在个人描述中看到.其他一些例子:

  • [7.6.4.1 (fenv)]将当前浮点环境存储在object pointed to乘以envp

  • [7.12.6.4 (frexp)]将整数存储在整数object pointed toexp

  • [7.19.5.1 (fclose)]stream pointed tostream

Example 2 - printf

[7.19.6.1]表示大约%p:

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

NULL是一个有效的指针值,本节没有明确提到NULL是一种特殊情况,也没有明确说明指针必须指向对象.因此,它是被定义的行为.


1.除非标准作者站出来,或者除非我们能找到类似于rationale文档的东西来澄清问题

C++相关问答推荐

va_arg -Linux和Windows上的不同行为

C中的ATONE会扰乱SEN/CLUTE GMS应用程序中的其他字符串

如何解决C中的严格别名?

空指针的运行时强制转换

我无法让LLDB正确运行我的可执行文件

带有sigLongjMP中断I/O的异常处理程序

C语言编译阶段与翻译阶段的关系

在C++中通过空指针隐式访问常量变量的值

为什么cudaFree不需要数据 struct 的地址?

函数的限制限定指针参数允许优化调用方函数吗?

为什么中断函数会以这种方式影响数组?

Square不与Raylib一起移动

为什么我可以在GCC的标签后声明变量,但不能声明Clang?

C指针概念分段故障

C代码可以在在线编译器上运行,但不能在Leetcode上运行

为什么我的半数组测试和奇数组测试不起作用?(我使用Assert进行调试)

如何在C宏定义中包含双引号?

正在try 理解C++中的`正在释放的指针未被分配‘错误

Tcl_GetDoubleFromObj在列表的迭代中是一个缺点

如何在C中计算包含递增和递减运算符的逻辑表达式?