C语言自定义文件读取函数触发段错误的原因与解决
C语言配置读取函数段错误的原因与解决方法
问题背景与现象
我正在用C语言开发个人视频游戏项目,因硬编码值日益增多,决定将配置数据存入文本文件动态读取。自行实现了一个返回void指针的读取函数,通过Return_Type枚举指定返回类型,但调用时触发段错误。
预期配置文件为每行变量名=值的格式,示例:
name=main_character money=1000 damage_multiplier=1.2
使用的枚举类型:
typedef enum{integer, string, floating,struct_limb,struct_attributes}Return_Type;
待读取的生物配置文件内容:
species=0 name=Short-faced bear representation=a id=0 limb_count=7 limbs=[[0,0,100,100],[1,0,100,100],[2,0,100,100],[2,0,100,100],[3,0,100,100],[3,0,100,100],[4,0,100,100]] weight=900 height=3.4 attributes=[12,12,12,12,12,12,12,12] max_health=100 max_carry=100 preferred_attack_type=1
读取函数(仅保留整数类型处理逻辑):
void *ir_readin_data(char *file_path, char *variable, Return_Type expected_type){ void *value; FILE *fp = fopen(file_path, "r"); char * line = NULL; size_t len = 0; while((getline(&line, &len, fp)) != -1){ char *variable_pointer = strstr(line, variable); if(variable_pointer != NULL){ char *value_as_str = strtok(strchr(line, '=')+1, "\n"); if(expected_type == integer){ int value_as_integer = atoi(value_as_str); value = &value_as_integer; fclose(fp); return value; } } } }
调用方式:
*(int *)ir_readin_data(creature_file_path,"species", integer);
运行触发段错误,尝试过传入void指针、返回char指针、使用union等方法均无效。
问题根源
段错误的核心原因是返回了栈上局部变量的地址:函数内的value_as_integer是栈内存中的局部变量,当函数执行完成返回后,对应的栈帧会被销毁,该地址变为野指针,后续访问时会触发非法内存操作,导致段错误。
解决方案
改用malloc在堆内存中分配存储空间,堆内存不会随函数返回而释放,调用者可安全访问数据,使用完毕后需手动调用free释放内存,避免内存泄漏。
修改后的读取函数(完善整数类型处理逻辑):
void *ir_readin_data(char *file_path, char *variable, Return_Type expected_type){ void *value = NULL; // 初始化指针,避免未定义行为 FILE *fp = fopen(file_path, "r"); if (!fp) { // 处理文件打开失败的情况 return NULL; } char *line = NULL; size_t len = 0; ssize_t read; while((read = getline(&line, &len, fp)) != -1){ char *variable_pointer = strstr(line, variable); if(variable_pointer != NULL){ char *equals_pos = strchr(line, '='); if (!equals_pos) { // 处理无等号的无效行 continue; } char *value_as_str = strtok(equals_pos + 1, "\n"); if(expected_type == integer){ int *ptr = malloc(sizeof(int)); if (!ptr) { // 处理内存分配失败的情况 fclose(fp); free(line); return NULL; } *ptr = atoi(value_as_str); value = ptr; break; // 找到目标变量后跳出循环,提升效率 } } } fclose(fp); free(line); // 释放getline分配的内存 return value; }
正确调用方式(记得释放内存):
int *species = (int *)ir_readin_data(creature_file_path, "species", integer); if (species != NULL) { printf("Species: %d\n", *species); free(species); // 使用完毕后释放堆内存 }
额外优化建议
- 添加错误处理:原代码未处理文件打开失败、无等号行、内存分配失败等情况,完善后提升代码健壮性
- 释放
getline内存:getline动态分配的内存必须手动free,避免内存泄漏 - 提前终止循环:找到目标变量后立即跳出循环,减少不必要的文件读取操作
- 扩展其他类型:后续支持字符串、浮点数、结构体时,同样需使用堆内存分配(如字符串用
strdup或malloc+strcpy)
内容的提问来源于stack exchange,提问作者Anton Nørgaard
相关产品推荐
相关产品推荐

