出于某种原因,加入\n
到printf()
改变的下面的代码的行为.没有\n
打印(null)
的代码而\n
导致的代码Segmentation fault
.
Printf.c
#include <stdio.h>
int main(int argc, char* argv[]){
printf("%s", argv[1]);
}
Run Code Online (Sandbox Code Playgroud)
Printf.c - 输出
$ gcc -o Printf Printf.c
$ ./Printf
(null)
Run Code Online (Sandbox Code Playgroud)
Printf_Newline.c
#include <stdio.h>
int main(int argc, char* argv[]){
printf("%s\n", argv[1]);
}
Run Code Online (Sandbox Code Playgroud)
Printf_Newline.c - 输出
$ gcc -o Printf_Newline Printf_Newline.c
$ ./Printf_Newline
Segmentation fault (core dumped)
Run Code Online (Sandbox Code Playgroud)
我很想知道这背后的原因.
小智 55
两者都是未定义的行为,所以答案可能会在这里停止.
但至少有一个解释输出(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,所以你也可以体验其他任何东西.
chq*_*lie 11
如果程序没有给出任何命令行参数,代码在两种情况下都有未定义的行为,因此任何事情都可能发生.
既然你很好奇(对你有好处!),这里有一个你观察到的可能的解释:
printf("%s\n", argv[1]);
可以由编译器优化成puts(argv[1]);
,而printf("%s", argv[1]);
仍然调用printf()
.
printf
接受空指针作为%s
转换参数的一些实现,因此输出(null)
.
puts()
具有未指定的空指针行为,在您的情况下是分段错误.
尝试编译两个没有任何优化(-O0
),看看你是否得到(null)
输出\n
.
您可以使用godbolt的编译器资源管理器,看看如何clang
使用-O0更改行为,但不能gcc
.