遇到一个有趣的面试问题:
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
首先,第一件事printf是:期望其%s参数使用有效的(即非NULL)指针,因此将NULL正式传递给它是不确定的。它可能会显示“(null)”,也可能会删除硬盘上的所有文件- 就ANSI而言,这是正确的行为(至少,这是Harbison和Steele告诉我的。)
printf
话虽这么说,是的,这确实是奇怪的行为。事实证明,发生的事情是当您执行以下简单操作时printf:
printf("%s\n", NULL);
GCC是( 啊哈 )足够聪明,解构到一个呼叫此 puts。第一个printf,这个:
puts
printf("test %s\n", NULL);
非常复杂,以至于gcc会发出对real的调用 printf。
(注意,gcc printf在编译时会发出有关无效参数的警告。这是因为它很早以前就已经开发出了解析*printf格式字符串的功能。)
*printf
您可以自己编译该-save-temps选项,然后查看生成的.s文件,从而自己查看。
-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
奇怪的是它不会打印以下换行符。好像已经弄清楚这将导致段错误,所以它不会打扰。(它有它-当我编译它时警告我。)