加载文本文件到二维数组后与字面量对比出现段错误的原因排查
为什么会出现段错误?
你的代码触发段错误的核心问题是没搞懂C语言的指针传递逻辑,再加上几个内存分配的小疏漏,具体拆解如下:
1. 指针参数的传递错误(最关键)
C语言里函数参数是值传递——你调用load(list)时,外部的list指针会被复制一份,作为函数内部的局部变量list。当你在load里执行list = malloc(MAX_NUM_LINES*sizeof(char*));,只是修改了这个局部副本的指向,完全不会影响外部原本的list变量!
这就导致:调用完load后,你外部的list仍然是未初始化的野指针。此时尝试访问list[0]或者*list[0],本质是在访问随机的非法内存地址,直接触发段错误。
2. 字符串内存分配的遗漏
给每行分配内存时,你用了malloc(len * sizeof(char)),但strlen(line)返回的是字符串的实际长度(不包含末尾的\0终止符)。而strcpy需要把\0也复制过去,所以这里应该分配len + 1个字符的空间,否则会出现内存越界,可能破坏其他内存数据,引发未定义行为。
3. 其他潜在隐患
line_ct没有在load里声明为局部变量,如果它是全局变量,很容易被其他代码意外修改,导致逻辑混乱;- 没有检查
fopen的返回值,如果文件"list"不存在或者无法打开,fp会是NULL,后续调用fgets直接崩溃; fgets会把换行符也读入字符串,如果不需要的话可以手动去除(比如line[len-1] = '\0',前提是确认最后一个字符是换行),不过这不会直接导致段错误,但可能影响后续的字符串对比。
修复后的代码示例
方案1:用指针的指针(传递外部指针的地址)
这种方式能让函数直接修改外部的list变量:
#include <stdio.h> #include <stdlib.h> #include <string.h> void load(char ***list) { const int MAX_NUM_LINES = 1000; FILE *fp = fopen("list", "r"); if (!fp) { // 检查文件是否打开成功 perror("Failed to open file"); *list = NULL; return; } *list = malloc(MAX_NUM_LINES * sizeof(char*)); if (!*list) { // 检查malloc是否成功 perror("Failed to allocate memory"); fclose(fp); return; } int line_ct = 0; // 用局部变量避免全局污染 char line[256]; while (fgets(line, 256, fp) != NULL && line_ct < MAX_NUM_LINES) { int len = strlen(line); // 分配包含终止符的空间 (*list)[line_ct] = malloc((len + 1) * sizeof(char)); if (!(*list)[line_ct]) { perror("Failed to allocate line memory"); // 这里可以添加已分配内存的释放逻辑,避免泄漏 fclose(fp); return; } strcpy((*list)[line_ct], line); line_ct++; } fclose(fp); } int main() { char** list; load(&list); // 传递指针的地址 if (list && list[0]) { // 先确认指针有效再访问 if (strcmp(list[0], "aasdf") == 0) { // 注意:strcmp返回0才代表字符串相等 printf("win\n"); } printf("Line: %s\n", list[0]); // 记得后续要逐层释放内存:先free每行的字符串,再free二维数组本身 } return 0; }
方案2:让函数返回分配好的指针
这种方式更直观,也更容易理解:
#include <stdio.h> #include <stdlib.h> #include <string.h> char** load() { const int MAX_NUM_LINES = 1000; FILE *fp = fopen("list", "r"); if (!fp) { perror("Failed to open file"); return NULL; } char** list = malloc(MAX_NUM_LINES * sizeof(char*)); if (!list) { perror("Failed to allocate memory"); fclose(fp); return NULL; } int line_ct = 0; char line[256]; while (fgets(line, 256, fp) != NULL && line_ct < MAX_NUM_LINES) { int len = strlen(line); list[line_ct] = malloc((len + 1) * sizeof(char)); if (!list[line_ct]) { perror("Failed to allocate line memory"); // 释放已分配的内存,避免泄漏 for (int i = 0; i < line_ct; i++) { free(list[i]); } free(list); fclose(fp); return NULL; } strcpy(list[line_ct], line); line_ct++; } fclose(fp); return list; } int main() { char** list = load(); if (list && list[0]) { if (strcmp(list[0], "aasdf") == 0) { printf("win\n"); } printf("Line: %s\n", list[0]); // 记得释放内存 } return 0; }
最后提醒:使用完动态分配的内存后,一定要记得逐层释放(先释放每行的字符串,再释放二维数组本身),避免内存泄漏。
内容的提问来源于stack exchange,提问作者Romanian Strife
相关产品推荐
相关产品推荐

