使用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

