[英]Execution of printf() and Segmentation Fault
#include<stdio.h>
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错误(显然我不能在常量char数组上写)。 那么为什么不执行第6行的printf()呢?
这是由于stdout
流缓冲。 除非您执行fflush(stdout)
或打印换行符"\\n"
,否则输出可能会被缓冲。
在这种情况下,在刷新并打印缓冲区之前就存在段错误。
您可以尝试以下方法:
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";
在回应中,我使用了一些“可能”“应该”。 该行为取决于您的OS,编译器和编译设置(链接器脚本定义了各个部分)。
新增中
-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
调用之前,段错误就可能发生!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.