2017-08-17 25 views
22

由于某些原因,将\n添加到printf()会更改以下代码的行为。没有\n的代码打印(null)而代码\n导致Segmentation fault将换行符添加到printf()更改代码行为

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) 

我很想了解这背后的原因。

+44

未定义的行为未定义。 – alk

+0

请解释你使用哪种编译器(* gcc *哪个版本),以及在哪种操作系统下,环境信息在这种情况下非常相关。 –

+4

谁知道当你的代码有一个错误会导致未定义的行为时会发生什么。也许下次你运行它时,世界将会结束 - 最好修正错误:) –

回答

54

两者都是未定义的行为,所以答案可以在这里停下来。

但是,至少有一个(null)的输出解释。这是glibc(GNU C库)的扩展。通过0%sprintf()被认为是undefined在C标准中因此很可能导致崩溃glibc的开发者决定做​​一些有意义的事情。

原因第二崩溃仍然是与换行,编译器决定优化:代替printf("%s\n", argv[1]),它执行puts(argv[1]),根据C标准,因此允许的优化,其在语义上等同。但是glibc s“(null)-trick”仅在printf()中实现。

有没有在你的程序中的其它未定义行为:你可能访问argv出界的。我们不能保证您在argv[i]时可以找到什么样的价值,i > argc。有一个机会argc可能是0,所以你可以体验其他任何以及。

+0

你从哪里得到这个“glibc”的东西? o__O – haccks

+11

仅当'i> argc'(严格大于等于) - argv [argc]'保证为空指针时,才会导致未定义的行为。但是在这个程序中我没有看到任何'argc> = 1'的检查。 –

+0

这很好,我不会想到它,但看着生成的程序集确认你说的是正确的。在存在''\ n“'的情况下,格式字符串根本不出现在生成的程序集中。 –

2

无参数执行argv[1]应为NULL指针。随着argv[1]NULL

printf("%s", argv[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

+0

是的,'--O0'标志没有使任何行为改变 – Beginner

+2

@Beginner:它实际上取决于你的编译器,最近版本的'gcc'似乎甚至在不告诉'-O0'的情况下优化'printf',而'clang'确实保持对'printf()'的调用。 – chqrlie