fgets触发动态结构体数组重复realloc及初始化异常问题
结构体数组扩容与数据存储异常问题
问题描述
用calloc初始化结构体数组,每次调用fgets读取文本文件一行就触发realloc扩容并初始化新元素。当前出现以下问题:
- 生成的结构体数组元素数量为预期的两倍,仅每隔一个元素存储正确数据,其余元素为空或数据错误。
add_struct_to_array函数调用次数是预期的两倍;尝试用strcspn处理换行后元素数量恢复正常,但仍只有每隔一个元素有正确数据。
文本文件每行格式示例:
A X C Z B Y ...
原代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #define GROWBY 100 #define BUFFER 100 typedef struct a_struct { char firstChar[2]; char secondChar[2]; int value; } TheStruct; TheStruct * new_struct(void) { TheStruct *aStruct = NULL; aStruct = calloc(1, sizeof(TheStruct) + 1); return aStruct; } TheStruct * add_struct_to_array(TheStruct * aStruct, int *numElements, int *bufferLen) { char *init = NULL; if (*numElements >= *bufferLen) { *bufferLen += GROWBY; TheStruct *newStruct = realloc(aStruct, *bufferLen * (sizeof(TheStruct) + 1)); if (newStruct == NULL) { free(aStruct); printf("Error: Memory could not be allocated."); exit(EXIT_FAILURE); } else if (aStruct != newStruct) { aStruct = newStruct; } newStruct = NULL; init = NULL; } *numElements += 1; char first[2] = { "0" }; char second[2] = { "0" }; strcpy(aStruct[*numElements].firstChar, first); strcpy(aStruct[*numElements].secondChar, second); aStruct[*numElements].value = 0; return aStruct; } void parse_buffer(TheStruct * aStruct, char *buffer, int *numElements) { char charOne[2] = "", charTwo[2] = ""; sscanf(buffer, "%s %s", charOne, charTwo); strcpy(aStruct[*numElements].firstChar, charOne); strcpy(aStruct[*numElements].secondChar, charTwo); if (*charOne == 'A') { if (*charTwo == 'X') { aStruct[*numElements].value = 6; } else if (*charTwo == 'Y') { aStruct[*numElements].value = 8; } else if (*charTwo == 'Z') { aStruct[*numElements].value = 1; } } else if (*charOne == 'B') { if (*charTwo == 'X') { aStruct[*numElements].value = 1; } else if (*charTwo == 'Y') { aStruct[*numElements].value = 6; } else if (*charTwo == 'Z') { aStruct[*numElements].value = 8; } } else if (*charOne == 'C') { if (*charTwo == 'X') { aStruct[*numElements].value = 8; } else if (*charTwo == 'Y') { aStruct[*numElements].value = 1; } else if (*charTwo == 'Z') { aStruct[*numElements].value = 6; } } } void main() { TheStruct *aStruct = new_struct(); int structCreated = 0; int bufferLen = 0, numElements = 0; char buffer[BUFFER]; FILE *file = fopen("input.txt", "r"); while (fgets(buffer, sizeof(buffer), file) != NULL) { if (!structCreated) { structCreated = 1; numElements += 1; } else { aStruct = add_struct_to_array(aStruct, &numElements, &bufferLen); } parse_buffer(aStruct, buffer, &numElements); } fclose(file); free(aStruct); }
错误现象
Valgrind未检测到内存问题,但打印结构体数据时:
预期输出:
A Y 6
实际输出:
0 A Y 6
问题分析与修复
核心错误点
数组索引越界
- C语言数组索引从0开始,原代码中
numElements初始为0,第一次循环直接设为1,此时访问aStruct[1]已经越界(初始仅分配1个结构体空间)。 add_struct_to_array中先执行*numElements +=1,再访问aStruct[*numElements],同样越界,应该先初始化当前numElements对应的元素,再递增计数。
- C语言数组索引从0开始,原代码中
内存分配冗余计算
calloc和realloc中sizeof(TheStruct)+1是错误的,结构体的大小已经包含所有成员,额外加1会导致内存分配过量,还可能引发内存对齐问题。
计数逻辑混乱
main函数中第一次循环手动递增numElements,后续循环调用add_struct_to_array又会递增计数,导致每次循环(除第一次)都多创建一个元素,最终元素数量翻倍。
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #define GROWBY 100 #define BUFFER 100 typedef struct a_struct { char firstChar[2]; char secondChar[2]; int value; } TheStruct; // 初始化空数组,后续通过realloc动态扩容 TheStruct *new_struct(void) { return NULL; } TheStruct *add_struct_to_array(TheStruct *aStruct, int *numElements, int *bufferLen) { // 当现有元素数量达到缓冲区长度时扩容 if (*numElements >= *bufferLen) { *bufferLen += GROWBY; TheStruct *newStruct = realloc(aStruct, *bufferLen * sizeof(TheStruct)); if (newStruct == NULL) { free(aStruct); fprintf(stderr, "Error: Memory could not be allocated.\n"); exit(EXIT_FAILURE); } aStruct = newStruct; } // 初始化当前元素(索引为*numElements) strcpy(aStruct[*numElements].firstChar, "0"); strcpy(aStruct[*numElements].secondChar, "0"); aStruct[*numElements].value = 0; // 计数递增,后续parse使用递增前的索引 (*numElements)++; return aStruct; } void parse_buffer(TheStruct *aStruct, char *buffer, int elementIndex) { char charOne[2] = "", charTwo[2] = ""; sscanf(buffer, "%s %s", charOne, charTwo); strcpy(aStruct[elementIndex].firstChar, charOne); strcpy(aStruct[elementIndex].secondChar, charTwo); if (charOne[0] == 'A') { if (charTwo[0] == 'X') aStruct[elementIndex].value = 6; else if (charTwo[0] == 'Y') aStruct[elementIndex].value = 8; else if (charTwo[0] == 'Z') aStruct[elementIndex].value = 1; } else if (charOne[0] == 'B') { if (charTwo[0] == 'X') aStruct[elementIndex].value = 1; else if (charTwo[0] == 'Y') aStruct[elementIndex].value = 6; else if (charTwo[0] == 'Z') aStruct[elementIndex].value = 8; } else if (charOne[0] == 'C') { if (charTwo[0] == 'X') aStruct[elementIndex].value = 8; else if (charTwo[0] == 'Y') aStruct[elementIndex].value = 1; else if (charTwo[0] == 'Z') aStruct[elementIndex].value = 6; } } int main() { TheStruct *aStruct = new_struct(); int bufferLen = 0, numElements = 0; char buffer[BUFFER]; FILE *file = fopen("input.txt", "r"); if (!file) { fprintf(stderr, "Error: Cannot open file.\n"); exit(EXIT_FAILURE); } while (fgets(buffer, sizeof(buffer), file) != NULL) { // 为当前行添加新结构体元素 aStruct = add_struct_to_array(aStruct, &numElements, &bufferLen); // 获取当前元素的索引(numElements递增前的值) int currentIndex = numElements - 1; // 解析数据到当前元素 parse_buffer(aStruct, buffer, currentIndex); } // 测试打印(可选) for (int i = 0; i < numElements; i++) { printf("%s %s %d\n", aStruct[i].firstChar, aStruct[i].secondChar, aStruct[i].value); } fclose(file); free(aStruct); return 0; }
关键修改说明
- 初始化逻辑简化:
new_struct返回NULL,后续通过realloc动态扩容,避免初始分配单个元素导致的索引混乱。 - 索引与计数同步:
add_struct_to_array先初始化numElements对应的元素,再递增计数;parse_buffer直接传入当前元素的索引,避免依赖全局计数出错。 - 内存分配修正:去掉
sizeof(TheStruct)+1的冗余计算,直接使用结构体实际大小分配内存。 - main逻辑统一:所有行的处理都通过
add_struct_to_array创建元素,不再区分第一次循环,逻辑一致,避免计数错误。
内容的提问来源于stack exchange,提问作者Lawro
相关产品推荐
相关产品推荐

