#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故障(显然我不能在常量字符数组上写入)。那么为什么不执行第6行的printf()?
答案 0 :(得分:39)
这是由于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)
答案 1 :(得分:10)
首先,您应该使用“\ 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部分中进行的,它是读写的。 顺便说一下这个例子有效。
答案 2 :(得分:5)
您遇到分段错误的原因是C字符串文字只能根据C标准读取,并且您试图在文字数组“Vikram”的第二个元素上写“s”。
您没有输出的原因是因为您的程序正在缓冲其输出并在有机会刷新其缓冲区之前崩溃。除了提供像printf(3)这样的友好格式化函数之外,stdio库的目的是通过缓冲内存缓冲区中的数据来减少I / O操作的开销,并且只在必要时刷新输出,并且仅偶尔执行输入而不是不断。在一般情况下,实际输入和输出不会在您调用stdio函数时发生,但仅在输出缓冲区已满(或输入缓冲区为空)时才会发生。
如果设置了FILE对象以使其不断刷新(如stderr),情况就会略有不同,但总的来说,这就是要点。
如果您正在调试,最好fprintf到stderr以确保在崩溃之前刷新调试打印输出。
答案 3 :(得分:1)
默认情况下,当stdout
连接到终端时,流是行缓冲的。在实践中,在您的示例中,缺少'\n'
(或显式流刷新)是您无法打印字符的原因。
但理论上,未定义的行为不受限制(来自标准&#34;行为[...],本国际标准没有强制要求&#34; )并且甚至可能发生段错误在未定义的行为发生之前,例如在第一次printf
调用之前!