You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题分析与修复

核心错误点

  1. 数组索引越界

    • C语言数组索引从0开始,原代码中numElements初始为0,第一次循环直接设为1,此时访问aStruct[1]已经越界(初始仅分配1个结构体空间)。
    • add_struct_to_array中先执行*numElements +=1,再访问aStruct[*numElements],同样越界,应该先初始化当前numElements对应的元素,再递增计数。
  2. 内存分配冗余计算

    • calloc和realloc中sizeof(TheStruct)+1是错误的,结构体的大小已经包含所有成员,额外加1会导致内存分配过量,还可能引发内存对齐问题。
  3. 计数逻辑混乱

    • 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 06:20:48