执行printf()和分段错误

#include int main() { char *name = "Vikram"; printf("%s",name); name[1]='s'; printf("%s",name); return 0; } 

终端上没有打印输出,只是出现分段故障。 但是当我在GDB中运行它时,我得到了以下 –

 Program received signal SIGSEGV, Segmentation fault. 0x0000000000400525 in main () at seg2.c:7 7 name[1]='s'; (gdb) 

这意味着程序在第7行接收SEG错误(显然我不能在常量字符数组上写入)。 那么为什么不执行第6行的printf()?

这是由于stdout流缓冲。 除非你执行fflush(stdout)或打印换行符"\n" ,否则输出可能会被缓冲。

在这种情况下,在刷新和打印缓冲区之前,它是segfaulting。

您可以尝试这样做:

 printf("%s",name); fflush(stdout); // Flush the stream. name[1]='s'; // Segfault here (undefined behavior) 

要么:

 printf("%s\n",name); // Flush the stream with '\n' name[1]='s'; // Segfault here (undefined behavior) 

首先,你应该用“\ n”(或至少是最后一个)结束你的printfs。 但这与段错误无关。

当编译器编译你的代码时,它会将二进制文件分成几个部分。 有些是只读的,有些是可写的。 写入只读部分可能会导致段错误。 字符串文字通常放在一个只读部分(gcc应该把它放在“.rodata”中)。 指针名称指向该ro部分。 因此你必须使用

 const char *name = "Vikram"; 

在我的回答中,我使用了一些“可能”“应该”。 行为取决于您的操作系统,编译器和编译设置(链接器脚本定义部分)。

添加

 -Wa,-ahlms=myfile.lst 

到gcc的命令行生成一个名为myfile.lst的文件,其中包含生成的汇编程序代码。 在顶部你可以看到

  .section .rodata .LC0: .string "Vikram" 

这表明字符串在Vikram中。

使用相同的代码(必须在全局范围内,否则gcc可能将其存储在堆栈中,注意它是一个数组而不是指针)

 char name[] = "Vikram"; 

产生

  .data .type name, @object .size name, 7 name: .string "Vikram" 

语法有点不同,但现在看看它是如何在.data部分中进行的,这是读写的。 顺便说一句这个例子是有效的。

你得到分段错误的原因是C字符串文字是根据C标准只读的,你试图在文字数组“Vikram”的第二个元素上写’s’。

你没有得到输出的原因是因为你的程序在它有机会刷新缓冲区之前缓冲输出并崩溃。 除了提供像printf(3)这样的友好格式化函数之外,stdio库的目的是通过缓冲内存缓冲区中的数据来减少I / O操作的开销,并且只在必要时刷新输出,并且仅偶尔执行输入而不是不断。 在一般情况下,实际输入和输出不会在您调用stdio函数时发生,但仅在输出缓冲区已满(或输入缓冲区为空)时才会发生。

如果设置了FILE对象以使其不断刷新(如stderr),情况就会略有不同,但总的来说,这就是要点。

如果您正在调试,最好fprintf到stderr以确保在崩溃之前刷新调试打印输出。

默认情况下,当stdout连接到终端时,流是行缓冲的。 在实践中,在您的示例中,缺少'\n' (或显式流刷新)是您不能打印字符的原因。

但理论上,未定义的行为不受限制(来自标准“行为[…],本国际标准不强制要求” )并且甚至在未定义行为发生之前就会发生段错误,例如在第一次printf调用之前!