首先要做的事情:printf
期望其 %s 参数的有效(即非 NULL)指针,因此将其传递 NULL 是正式未定义的。它可能会打印“(null)”,也可能会删除硬盘驱动器上的所有文件——就 ANSI 而言,这两种行为都是正确的(至少,Harbison 和 Steele 是这样告诉我的。)
话虽如此,是的,这确实是一种奇怪的行为。事实证明,当你做这样一个简单的事情时,正在发生的事情是printf
:
printf("%s\n", NULL);
gcc足够聪明,可以将其解构为对
puts
. 第一个printf
,这个:
printf("test %s\n", NULL);
足够复杂,以至于 gcc 将改为发出对 real 的调用
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
奇怪的是它不打印以下换行符。就好像它已经发现这会导致段错误,所以它不会打扰。(它有——当我编译它时它警告我。)