在 C 中使用 fgets 时出现“分段错误:11”

Posted

技术标签:

【中文标题】在 C 中使用 fgets 时出现“分段错误:11”【英文标题】:"Segmentation fault: 11" when using fgets in C 【发布时间】:2022-01-14 21:16:50 【问题描述】:

我正在用 C 语言编写一个命令行工具来控制 UNDERTALE 保存文件(因为为什么不这样做),每当我从用户那里获取输入时,我都会遇到分段错误。

这是我所有的代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>

#define KILOBYTE 1024

/* The 'm()' is so i know what line the fault happens on */

void m(void) 
        printf("X");


int main(int argc, char**argv) 
    FILE *file;

    if (file = fopen("~/.undersave", "r")) 
        // Do some code that I haven't written yet
    
    else 
        char path[KILOBYTE] = "";
        printf("It seems that this is your fist time using UNDERSAVE.\nWe just need to go through some quick installation steps.\nWhere do you want your UNDERTALE save folder to be located? >>> ");
        m();
        fgets(path, KILOBYTE, stdin);
        /*
              The fault happens here, when it asks me, 
              i put in the input like normal, but
              when I press enter, I get a fault before
              it prints 'X'
         */
        m();
        mkdir(path, 0777);
        m();
        file = fopen("~/.undersave", "w");
        m();
        fprintf(file, "%s", path);
        m();
        fclose(file);
    
      
    return 0;

【问题讨论】:

Re "'m()' 让我知道故障发生在哪条线路上",oof。学习使用调试器。 gdb 可以很容易地给你一个堆栈跟踪(命令是bt IIRC)。而且,如果您使用gccclang,您应该会发现-fsanitize=address 非常有用。 printf 使用缓冲输出,因此除非刷新输出缓冲区,否则您将看不到任何内容。通常,您只需在 printf 末尾使用换行符 ('\n') 即可。我推荐void m(int location) printf("X%d\n", location); ,然后将调用更改为m(1)m(2)等。 另见how to remove the newline that fgets puts in the buffer。 【参考方案1】:

很可能两个对fopen 的调用都失败了,因为在类Unix 系统上~-expansion 是shell 的一个特性,不适用于直接使用fopen 或@987654325 打开文件的程序@。如果要打开相对于主目录的文件,请包含完整路径,或使用getenv 读取HOME 环境变量并在程序中构造路径。或者,更好的是,使文件名成为命令行参数;这些参数在您的程序看到它们之前由 shell 扩展,因此 ~ 扩展将起作用。

因此,fopen 用于写入返回 NULL,您无需对其进行测试(一个更严重的错误)。然后你尝试将fprintf 指向一个空文件指针,这自然会崩溃。

此外,您的 printf("X") 也不是一个好的诊断工具,因为到终端的输出通常是缓冲的。在打印新行或尝试从标准输入读取或发生其他一些特殊情况之前,X 实际上不会被写入屏幕。所以我怀疑程序实际上崩溃的时间比你想象的要晚,在fprintf 而不是fgets 上。查看Why does printf not flush after the call unless a newline is in the format string?,然后将fflush(stdout) 放在printf 之后的m() 函数中。

【讨论】:

如果有的话,做一些我还没写的代码,最好加进去。 哦等等,我现在明白了。这不是 fgets,因为 'm();'没有被冲走。真正的问题在于'fprintf(file,"%s",path);',未创建文件夹的原因是路径不存在。 (〜不存在)所以我只是假设它正在停止它。

以上是关于在 C 中使用 fgets 时出现“分段错误:11”的主要内容,如果未能解决你的问题,请参考以下文章

尝试使用指向 char* 的指针时出现总线错误 10 或分段错误 11,具体取决于封装

在 Swift 中使用 Set 时出现编译器分段错误

在C中调用fgets时出现分段错误

获取交换功能的分段错误 11

分段错误:11 - C 函数

通过共享内存和管道的 IPC 给出分段错误:C 中的 11