向printf()添加换行符会改变代码行为

时间:2017-08-17 14:45:19

标签: c linux printf

出于某种原因,将\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)

我很想知道背后的原因。

3 个答案:

答案 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]);

将调用未定义的行为。