简体   繁体   English

printf()的执行和分段错误

[英]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;
}

There is no output printed on terminal and just get segmentation fault. 终端上没有输出输出,只会出现分段错误。 But when I run it in GDB, I get following - 但是,当我在GDB中运行它时,我得到的是-

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

This means program receive SEG fault on 7th line (obviously I can't write on constant char array) . 这意味着程序在第7行收到SEG错误(显然我不能在常量char数组上写)。 Then why printf() of line number 6 is not executed ? 那么为什么不执行第6行的printf()呢?

This is due to stream buffering of stdout . 这是由于stdout流缓冲。 Unless you do fflush(stdout) or you print a newline "\\n" the output is may be buffered. 除非您执行fflush(stdout)或打印换行符"\\n" ,否则输出可能会被缓冲。

In this case, it's segfaulting before the buffer is flushed and printed. 在这种情况下,在刷新并打印缓冲区之前就存在段错误。

You can try this instead: 您可以尝试以下方法:

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

or: 要么:

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

First you should end your printfs with "\\n" (or at least the last one). 首先,您应该以“ \\ n”(或至少最后一个)结束printfs。 But that is not related to the segfault. 但这与段错误无关。

When the compiler compiles your code, it splits the binary into several section. 当编译器编译您的代码时,它将二进制文件拆分为几个部分。 Some are read only, while other are writeable. 有些是只读的,而另一些是可写的。 Writing to an read only section may cause a segfault. 写入只读段可能会导致段错误。 String literals are usually placed in a read only section (gcc should put it in ".rodata"). 字符串文字通常放置在只读段中(gcc应将其放在“ .rodata”中)。 The pointer name points to that ro section. 指针名称指向该ro节。 Therefore you must use 因此,您必须使用

const char *name = "Vikram";

In my response I've used a few "may" "should". 在回应中,我使用了一些“可能”“应该”。 The behaviour depends on your OS, compiler and compilation settings (The linker script defines the sections). 该行为取决于您的OS,编译器和编译设置(链接器脚本定义了各个部分)。

Adding 新增中

-Wa,-ahlms=myfile.lst

to gcc's command line produces a file called myfile.lst with the generated assembler code. gcc的命令行生成包含生成的汇编代码的名为myfile.lst的文件。 At the top you can see 在顶部,您可以看到

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

Which shows that the string is in Vikram. 这表明该字符串在Vikram中。

The same code using (Must be in global scope, else gcc may store it on the stack, notice it is an array and not a pointer) 使用相同的代码(必须在全局范围内,否则gcc可能会将其存储在堆栈中,请注意它是数组而不是指针)

char name[] = "Vikram";

produces 产生

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

The syntax is a bit different but see how it is in .data section now, which is read-write. 语法略有不同,但是现在可以在.data节中以可读写的方式查看。 By the way this example works. 顺便说一下,此示例工作。

The reason you are getting a segmentation fault is that C string literals are read only according to the C standard, and you are attempting to write 's' over the second element of the literal array "Vikram". 出现分段错误的原因是,根据C标准只能读取C字符串文字,并且您试图在文字数组“ Vikram”的第二个元素上写入's'。

The reason you are getting no output is because your program is buffering its output and crashes before it has a chance to flush its buffer. 之所以没有输出,是因为您的程序正在缓冲其输出并在有机会刷新其缓冲区之前崩溃。 The purpose of the stdio library, in addition to providing friendly formatting functions like printf(3), is to reduce the overhead of i/o operations by buffering data in in-memory buffers and only flushing output when necessary, and only performing input occasionally instead of constantly. 除了提供友好的格式化功能(如printf(3))之外,stdio库的目的还在于通过将数据缓冲在内存中的缓冲区中以及仅在必要时仅刷新输出以及仅偶尔执行输入来减少I / O操作的开销。而不是不断。 Actual input and output will not, in the general case, occur at the moment when you call the stdio function, but only when the output buffer is full (or the input buffer is empty). 在通常情况下,实际的输入和输出不会在调用stdio函数时发生,而只会在输出缓冲区已满(或输入缓冲区为空)时发生。

Things are slightly different if a FILE object has been set so it flushes constantly (like stderr), but in general, that's the gist. 如果已设置FILE对象,以便不断刷新(如stderr),则情况会稍有不同,但是总的来说,这就是要点。

If you're debugging, it is best to fprintf to stderr to assure that your debug printouts will get flushed before a crash. 如果您正在调试,最好将fprintf转换为stderr,以确保在崩溃前刷新调试打印输出。

By default when stdout is connected to a terminal, the stream is line-buffered. 默认情况下,当stdout连接到终端时,流是行缓冲的。 In practice, in your example the absence of '\\n' (or of an explicit stream flush) is why you don't get the characters printed. 实际上,在您的示例中,没有'\\n' (或显式的流刷新)是为什么您无法打印字符的原因。

But in theory undefined behavior is not bounded (from the Standard "behavior [...] for which this International Standard imposes no requirements" ) and the segfault can happen even before the undefined behavior occurs, for example before the first printf call! 但是从理论上讲,未定义的行为是不受限制的(从标准“该国际标准对此没有施加要求的行为”开始 ),甚至在未定义的行为发生之前,例如在第一次printf调用之前,段错误就可能发生!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM