"Segmentation fault: 11" 在 C 中使用 fgets 时

"Segmentation fault: 11" when using fgets in C

我正在用 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;
}

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

所以 fopen 用于写入 returns 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) 放在 m() 函数中的 printf 之后。