什么是印刷NULL用的printf的%s的说明符的行为?(What is the behavior

2019-06-17 11:58发布

偶然发现了一个有趣的面试问题:

test 1:
printf("test %s\n", NULL);
printf("test %s\n", NULL);

prints:
test (null)
test (null)

test 2:
printf("%s\n", NULL);
printf("%s\n", NULL);
prints
Segmentation fault (core dumped)

虽然这可能在某些系统上运行良好,至少我的是扔分割故障。 什么是这种行为的最好解释? 上面的代码是C.

以下是我的gcc的信息:

deep@deep:~$ gcc --version
gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

Answer 1:

首先第一件事情: printf期待一个有效的(即非NULL)指针的%S参数,所以它传递一个NULL正式定义。 它可以打印“(空)”,或者它可能会删除硬盘上的所有文件 - 无论是正确的行为就ANSI而言(至少,这就是哈比森和斯蒂尔告诉我。)

话虽这么说,是啊,这真是奇怪的行为。 事实证明,这是怎么回事,就是当你做一个简单printf像这样:

printf("%s\n", NULL);

GCC是( 啊哈 )足够聪明,解构到一个呼叫该puts 。 第一个printf ,这样的:

printf("test %s\n", NULL);

够复杂的是GCC反而会发出真正的调用printf

(请注意,GCC发出有关警告无效printf在编译时的说法,这是因为它早已发展到解析能力*printf格式字符串。)

您可以通过编制看到这个自己-save-temps选项,然后翻翻导致.s文件。

当我编译第一个例子中,我得到:

movl    $.LC0, %eax
movl    $0, %esi
movq    %rax, %rdi
movl    $0, %eax
call    printf      ; <-- Actually calls printf!

(评论是由我所加)

但第二个产生的这样的代码:

movl    $0, %edi    ; Stores NULL in the puts argument list
call    puts        ; Calls puts

该奇怪的是,它不打印以下换行符。 就好像它是想通了,这会导致一个segfault所以它不打扰。 (它有 - 它提醒我,当我编译它。)



Answer 2:

至于C语言来讲,其原因是,你调用未定义行为,任何事情都有可能发生。

至于为什么发生这种情况,现代GCC优化机制printf("%s\n", x)puts(x)puts没有傻代码打印(null)时,看到一个空指针,而普遍的实现printf有这种特殊情况。 由于GCC不能优化(一般)不平凡的格式字符串这样, printf实际被调用时,格式字符串具有其他文字存在于它。



Answer 3:

7.1.4节(C99的C11或)说:

§7.1.4的库函数使用

¶1每个下列陈述适用,除非在下面的详细描述明确地指出,否则:如果一个参数传递给函数的值无效(如函数的域之外的值,或者的地址空间外的指针程序,或一个空指针,或指向当相应的参数不是常量限定不可修改的存储器)或类型(升级后)不通过与可变的参数数目的函数预期,该行为是未定义的。

由于规范printf()什么都不说,当你传递一个空指针,它的发生的事情%s说明符,这个行为是明确的定义。 (注意,传递一个空指针由被打印%p说明符不未定义行为)。

这里是“引经据典”的fprintf()家庭行为(C2011 -这是在C1999不同的区段号):

§7.21.6.1fprintf函数

s如果没有l长度改性剂的存在,该参数将是指向字符类型的数组的初始元素。 [...]

如果l长度改性剂的存在,该参数将是指向wchar_t的类型的数组的初始元素。

p的参数应是无效的指针。 指针的值被转换成打印字符的序列,在一个实现定义方式。

的详细规格s转换说明排除一个空指针是有效的,因为空指针不指向初始适当类型的数组的元素的可能性。 为规范p转换说明不需要空指针在什么特别指向和NULL因此有效。

许多实现打印一个字符串,如事实(null)传递空指针是一个善良是危险的依赖。 未定义行为的好处是,这种反应是允许的,但它不是必需的。 同样,崩溃是允许的,但不是必需的(更为可惜 - 如果他们宽容的系统端口工作,然后到其他不宽容系统的人得到咬伤)。



Answer 4:

NULL指针不指向任何地址,并试图打印它会导致不确定的行为。 未定义这意味着它是给你的编译器或C库,以决定何时试图打印NULL做什么。



文章来源: What is the behavior of printing NULL with printf's %s specifier?