出于某种原因,将\n
添加到printf()
会更改以下代码的行为。没有\n
的代码会打印(null)
,而带有\n
的代码会打印Segmentation fault
。
Printf.c
#include <stdio.h>
int main(int argc, char* argv[]){
printf("%s", argv[1]);
}
Printf.c - 输出
$ gcc -o Printf Printf.c
$ ./Printf
(null)
Printf_Newline.c
#include <stdio.h>
int main(int argc, char* argv[]){
printf("%s\n", argv[1]);
}
Printf_Newline.c - 输出
$ gcc -o Printf_Newline Printf_Newline.c
$ ./Printf_Newline
Segmentation fault (core dumped)
我很想知道背后的原因。
答案 0 :(得分:54)
两者都是未定义的行为,因此答案可能会在此停止。
但至少有(null)
输出的解释。这是glibc
(GNU C库)中的扩展。在0
中为%s
传递printf()
在C标准中被视为未定义,因此很可能导致崩溃。 glibc
的开发人员决定做一些有意义的事情。
然而第二次崩溃的原因是,使用换行符,编译器决定优化:而不是printf("%s\n", argv[1])
,它执行puts(argv[1])
,它在语义上等效于C标准,因此允许优化。但glibc
s“(null)-trick”仅在printf()
中实现。
您的计划中还有另一个未定义的行为:您可能会访问argv
越界。我无法保证argv[i]
i > argc
时您会找到什么价值。 argc
可能为0,因此您可以体验其他任何。
答案 1 :(得分:11)
如果程序没有给出任何命令行参数,代码在两种情况下都有未定义的行为,所以任何事情都可能发生。
既然你很好奇(对你有好处!),这里有一个潜在的解释:
printf("%s\n", argv[1]);
可以由编译器优化为puts(argv[1]);
,而printf("%s", argv[1]);
仍会调用printf()
。
printf
的某些实现接受空指针作为%s
转换的参数,因此输出(null)
。
puts()
对于空指针有未定义的行为,在您的情况下是分段错误。
尝试在没有任何优化(-O0
)的情况下进行编译,看看是否有(null)
输出\n
。
您可以使用godbolt's compiler explorer进行游戏,看看clang
如何通过-O0更改行为,而不是gcc
。
答案 2 :(得分:2)
不带参数argv[1]
的执行应为NULL
指针。 argv[1]
为NULL
printf("%s", argv[1]);
将调用未定义的行为。