C语言中使用realloc动态分配结构体数组触发SIGTRAP崩溃的原因
问题分析与修复方案
你的代码存在多个触发崩溃的问题,逐一拆解并修复:
1. 参数传递错误
函数参数User *users是值传递,函数内对users的地址修改不会同步到外部调用者。需要改用指针的指针(User **users),让外部能获取到重新分配后的内存地址。
2. 内存分配大小错误
你用sizeof(struct user *)分配内存,这是在分配指针的大小(通常4/8字节),而非User结构体本身的大小。正确写法是sizeof(User)或sizeof(struct user)。
3. feof循环条件的陷阱
!feof(file)作为循环条件会导致多读一次:当读到文件末尾时,feof不会立刻置位,直到下一次读取失败才会标记,这会让循环多执行一次,触发越界访问。应该用fgets的返回值作为循环终止条件。
4. realloc的安全问题
realloc可能返回NULL(内存分配失败),直接赋值给users会丢失原有内存地址,造成内存泄漏。必须先用临时变量接收结果,检查成功后再赋值。
5. 缺失关键错误检查
fopen失败后未终止函数,后续操作NULL指针会直接崩溃。strtok可能返回NULL(比如行格式不符合预期),直接访问会触发空指针引用,需要每次检查tok是否有效。
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> // 补全代码依赖的结构体定义(根据原代码推断) typedef struct { int day; int month; int year; } Date; typedef struct user { char username[50]; char name[50]; char gender; Date birth_date; Date account_creation; char pay_method[20]; char account_status[20]; } User; void readsaveusers(char* filename, User **users, int *user_count) { FILE *file = fopen(filename, "r"); if (file == NULL) { printf("Error reading file\n"); *users = NULL; *user_count = 0; return; } char line[200]; *users = NULL; *user_count = 0; while (fgets(line, 200, file) != NULL) { // 去除行尾换行符,避免解析干扰 size_t len = strlen(line); if (len > 0 && line[len-1] == '\n') { line[len-1] = '\0'; } // 安全扩容:用临时变量接收realloc结果 User *temp = realloc(*users, sizeof(User) * (*user_count + 1)); if (temp == NULL) { printf("Memory allocation failed\n"); free(*users); // 释放已分配内存,防止泄漏 *users = NULL; *user_count = 0; fclose(file); return; } *users = temp; // 逐段解析并检查有效性 char *tok = strtok(line, ";"); if (tok == NULL) continue; strncpy((*users)[*user_count].username, tok, sizeof((*users)[*user_count].username)-1); (*users)[*user_count].username[sizeof((*users)[*user_count].username)-1] = '\0'; tok = strtok(NULL, ";"); if (tok == NULL) continue; strncpy((*users)[*user_count].name, tok, sizeof((*users)[*user_count].name)-1); (*users)[*user_count].name[sizeof((*users)[*user_count].name)-1] = '\0'; tok = strtok(NULL, ";"); if (tok == NULL) continue; (*users)[*user_count].gender = tok[0]; tok = strtok(NULL, "/"); if (tok == NULL) continue; (*users)[*user_count].birth_date.day = atoi(tok); tok = strtok(NULL, "/"); if (tok == NULL) continue; (*users)[*user_count].birth_date.month = atoi(tok); tok = strtok(NULL, ";"); if (tok == NULL) continue; (*users)[*user_count].birth_date.year = atoi(tok); tok = strtok(NULL, "/"); if (tok == NULL) continue; (*users)[*user_count].account_creation.day = atoi(tok); tok = strtok(NULL, "/"); if (tok == NULL) continue; (*users)[*user_count].account_creation.month = atoi(tok); tok = strtok(NULL, ";"); if (tok == NULL) continue; (*users)[*user_count].account_creation.year = atoi(tok); tok = strtok(NULL, ";"); if (tok == NULL) continue; strncpy((*users)[*user_count].pay_method, tok, sizeof((*users)[*user_count].pay_method)-1); (*users)[*user_count].pay_method[sizeof((*users)[*user_count].pay_method)-1] = '\0'; tok = strtok(NULL, ";"); if (tok == NULL) continue; strncpy((*users)[*user_count].account_status, tok, sizeof((*users)[*user_count].account_status)-1); (*users)[*user_count].account_status[sizeof((*users)[*user_count].account_status)-1] = '\0'; (*user_count)++; } printf("Saved %d users\n", *user_count); fclose(file); } // 调用示例 int main() { User *users; int count; readsaveusers("users.txt", &users, &count); // 使用完成后记得释放内存 free(users); return 0; }
额外优化说明
- 改用
strncpy替代strcpy,避免字符串过长导致缓冲区溢出。 - 添加
user_count参数,让外部能直接获取读取到的用户数量。 - 处理
fgets读取到的换行符,避免后续解析出错。 - 内存分配失败时主动释放已分配内存,防止内存泄漏。
内容的提问来源于stack exchange,提问作者Karlofsky
相关产品推荐
相关产品推荐

