You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用fgets遍历文件向数组追加内容时出现段错误的问题求助

问题排查与修复方案

Hey,我来帮你捋捋这个问题——你遇到的段错误和奇怪的“array[3]能正常访问但前几个不行”的情况,本质是代码里有几个关键错误,还有个容易忽略的小细节,咱们一个个拆解:

核心问题1:空指针数组的越界访问

你定义的char *array[] = {};是一个大小为0的指针数组,在C语言里这种写法其实不符合标准(部分编译器可能睁一只眼闭一只眼,但行为完全是未定义的)。简单说,这个数组根本没有存储空间来存放你后续要添加的指针。

当你在循环里给array[i]赋值时,你其实是在往数组的边界外写数据,直接破坏了栈上的其他内存区域。有时候访问array[3]没崩纯粹是运气好——那块内存刚好还没被程序的关键数据占用,但这种情况完全不可靠,属于典型的未定义行为。

核心问题2:所有数组元素指向同一个缓冲区

第二个坑是:你把line的地址赋值给array[i],但line是栈上的固定大小数组,每次循环fgets都会把新行的内容覆盖到这个缓冲区里。就算数组有足够空间,最后array里的所有指针都会指向同一个line的地址,打印出来的内容全是最后一行的结果,这肯定不是你想要的。

额外小细节:数组下标搞反了

你的代码里i初始值是0,循环里先执行i++再赋值,这导致array[0]从来没被赋值过,而你尝试访问的array[1]其实是第一个读取的行——但因为数组本身没有空间,直接触发了段错误。


现在给你两种可行的修复方案:

方案一:动态扩容指针数组(更灵活,推荐)

我们用malloc和realloc来动态调整数组的大小,同时用strdup(或者malloc+strcpy)复制每行的内容,这样每个数组元素都有独立的存储空间:

#include "library.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
    FILE *file = fopen("words.txt", "r");
    // 务必检查文件是否打开成功
    if (file == NULL) {
        perror("Failed to open words.txt");
        return 1;
    }

    char line[100];
    char **array = NULL; // 初始化为空指针
    int line_num = 0;

    while(fgets(line, sizeof(line), file) != NULL) {
        // 扩容指针数组:每次增加一个指针的空间
        char **temp = realloc(array, (line_num + 1) * sizeof(char *));
        if (temp == NULL) {
            perror("Memory allocation failed");
            // 清理已分配的内存,避免泄漏
            for (int j = 0; j < line_num; j++) {
                free(array[j]);
            }
            free(array);
            fclose(file);
            return 1;
        }
        array = temp;

        // 复制当前行到新内存
        array[line_num] = strdup(line);
        if (array[line_num] == NULL) {
            perror("Failed to copy line");
            // 同样要清理资源
            for (int j = 0; j < line_num; j++) {
                free(array[j]);
            }
            free(array);
            fclose(file);
            return 1;
        }
        line_num++;
    }

    // 测试访问(注意下标从0开始)
    if (line_num >= 3) {
        printf("Line 1 = %s", array[0]);
        printf("Line 2 = %s", array[1]);
        printf("Line 3 = %s", array[2]);
    }

    // 清理所有动态分配的内存
    for (int j = 0; j < line_num; j++) {
        free(array[j]);
    }
    free(array);
    fclose(file);
    return 0;
}

方案二:先统计行数再分配数组(适合已知文件规模的场景)

如果你可以先读一遍文件统计行数,就能直接分配足够大的指针数组,避免动态扩容的操作:

#include "library.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
    FILE *file = fopen("words.txt", "r");
    if (file == NULL) {
        perror("Failed to open words.txt");
        return 1;
    }

    // 第一步:统计文件行数
    char line[100];
    int line_count = 0;
    while(fgets(line, sizeof(line), file) != NULL) {
        line_count++;
    }
    // 把文件指针移回开头
    rewind(file);

    // 分配足够大的指针数组
    char **array = malloc(line_count * sizeof(char *));
    if (array == NULL) {
        perror("Memory allocation failed");
        fclose(file);
        return 1;
    }

    // 第二步:读取每行并复制
    int i = 0;
    while(fgets(line, sizeof(line), file) != NULL) {
        array[i] = strdup(line);
        if (array[i] == NULL) {
            perror("Failed to copy line");
            for (int j = 0; j < i; j++) {
                free(array[j]);
            }
            free(array);
            fclose(file);
            return 1;
        }
        i++;
    }

    // 测试访问
    if (line_count >= 3) {
        printf("Line 1 = %s", array[0]);
        printf("Line 2 = %s", array[1]);
        printf("Line 3 = %s", array[2]);
    }

    // 清理资源
    for (int j = 0; j < line_count; j++) {
        free(array[j]);
    }
    free(array);
    fclose(file);
    return 0;
}

最后再提几个注意事项:

  • 始终检查内存分配和文件操作的返回值:忽略这些错误会导致程序出现各种莫名其妙的崩溃,比如文件不存在时fopen返回NULL,直接使用会触发段错误。
  • C语言数组下标从0开始:别再像原代码那样搞反了,不然会出现“明明有数据但访问不到”的问题。
  • 记得释放动态内存:所有用malloc/strdup分配的内存都要手动释放,避免内存泄漏。

内容的提问来源于stack exchange,提问作者bas kruithof

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 21:34:07