【问题标题】:Why is segmentation fault happening in this C program?为什么这个 C 程序会发生分段错误?
【发布时间】:2017-10-14 09:25:33
【问题描述】:

我编写了一个简单的 C 程序来从 .txt 文件中读取数据并将其打印到屏幕上。

FILE *fp = fopen("words.txt", "r");

char buffer[3];

while (fscanf(fp, "%s", buffer) != EOF)
printf(" %s\n ", buffer);

printf("\n");

word.txt 里面只写了 123。现在,我运行程序时得到的输出是

123
Segmentation fault

为什么会出现分段错误,这意味着什么?

【问题讨论】:

  • char buffer[3]; --> char buffer[4];, fscanf(fp, "%s", buffer) --> fscanf(fp, "%3s", buffer)
  • 您忘记了字符串的“\0”,word.txt 内容将在您的程序中转换为“123\0”。将缓冲区[3]更改为缓冲区[4];
  • 将缓冲区 [3] 更改为缓冲区 [256] 并停止 bean 计数,(除非您在某些 RAM 受限的嵌入式系统上)。

标签: c arrays file segmentation-fault


【解决方案1】:

C printf()scanf()fscanf(),实际上大多数字符串功能都适用于 空终止字符串

在计算机编程中,以空字符结尾的字符串是存储为包含字符的数组并以空字符结尾的字符串('\0',在 ASCII 中称为 NUL)。

因此,当您的输入包含 3 个字符时,您还必须为空字符保留空间。 在您的示例中,缓冲区的大小应至少为 4。

为防止用户出错,您始终可以使用以下方法限制输入的大小:

char buffer[16]; // An array of 16 for example
scanf("%15s", buffer); // Must also reserve space for the null

请注意,您得到的Segmentation fault 不是,因为fscanf() 函数试图将NULL 写入缓冲区的第4 个字符。 这只会覆盖堆栈上的一些局部变量。

错误很可能是由于将NULL写入存储指向文件的指针的内存位置(FILE *fp),在下一次迭代时产生Segmentation fault,当我们尝试从再次打开文件。

为了说明这一点,您的堆栈如下所示:

| buffer[0] | buffer[1] | buffer[2] | fp | fp | fp | fp | ... 

fscanf() 尝试将"123\0" 写入缓冲区,因此它从缓冲区的开头开始,并将 1,2,3 分别放在位置 0,1,2 中。但是NULL 字符恰好位于fp 的一部分所在的位置,从而损坏了指针。

【讨论】:

  • 我总是很高兴听到批评,所以很高兴知道为什么会被否决。
【解决方案2】:

您的字符串缓冲区需要为指示字符串结尾的 '\0' 字符留出空间:字符串 "123" 实际上是 ['1', '2', '3', '\0'] . fscanf 会自动添加 '\0'。

【讨论】:

    【解决方案3】:

    您的具体问题是 nul 终止 字符数组。

    也就是说,3个字符的字符串需要4个字节的char缓冲区来存储:

    ------------------
    | 1 | 2 | 3 | \0 |
    ------------------
    

    所以

    char buffer[3];
    

    必须

    char buffer[4];
    

    顺便说一句,这不是一个完全正确的解决方案,因为您必须声明您的数组足够大以包含可以在文件中检索到的最长字符串。

    【讨论】:

      【解决方案4】:

      这是因为 NULL 字符 \0 你的字符串不是 3 个字符长,但实际上是 4 个字符长。您的缓冲区太小

      只是一个建议,我认为使用 fscanf 并不是最好的解决方案

      #include <unistd.h>
      #include <stdio.h>
      #include <stdlib.h>
      
      int main()
      {
        FILE *f = fopen("words.txt", "rb");
        fseek(f, 0, SEEK_END);
        long fsize = ftell(f);
        fseek(f, 0, SEEK_SET);  //same as rewind(f);                                  
      
        char *string = malloc(fsize + 1);
        fread(string, fsize, 1, f);
        fclose(f);
      
        string[fsize] = 0;
        printf("%s\n", string);
      }
      

      使用 fread 是更好的解决方案

      【讨论】:

        猜你喜欢
        • 2019-04-17
        • 2014-03-31
        • 1970-01-01
        • 2015-07-23
        • 2011-01-27
        • 1970-01-01
        • 2015-09-29
        相关资源
        最近更新 更多