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

如何为结构体中不定大小数组正确使用malloc分配内存

内存分配错误排查:结构体动态数组问题

定义的结构体

struct KMData{
    int ndata;
    int dim;
    float **features;
    int *assigns;
    int *labels;
    int nlabels;
};

问题描述

编写kmdata_load函数时,尝试根据输入文件大小为结构体的features和labels分配内存,但打印features的大小仅为8,远小于预期。调整结构体内存分配方式为:

struct KMData *data = malloc(sizeof(struct KMData));
data->labels = malloc(lines * sizeof(int));

后,执行data->labels[data->ndata-1] = atoi(token);触发段错误,需排查内存分配中的问题。

完整函数代码

struct KMData kmdata_load(char *datafile) {

    ssize_t tokens;
    ssize_t lines;

    filestats(datafile, &tokens, &lines);

    struct KMData *data = malloc(sizeof(struct KMData) + (lines * sizeof(int)) + ((tokens - (2*lines)) * sizeof(float)));
    
    printf("tokens: %zd, lines: %zd\n", tokens, lines);

    data->labels = malloc(lines * sizeof(int));
    data->features = malloc((tokens - (2*lines)) * sizeof(float));

    ssize_t f_size = sizeof(*(data->features));
    printf("size of features: %zd\n", f_size);

    FILE *fin = fopen(datafile, "r");

    char line[3150];

    int i = 0;

    while (fgets(line, 3150, fin)) {

        data->ndata++;
        char *token = strtok(line, " \t");

        data->labels[data->ndata-1] = atoi(token);

        float feats[(tokens/lines)-2];

        int f = 0;
        token = strtok(NULL, " \t");
        while ((token = strtok(NULL, " \t"))) {
            feats[f] = atof(token);
           
            data->features[data->ndata-1][f] = atof(token);

            if(i==0){
                printf("token %d: %f\n", f, data->features[data->ndata-1][f]);
            }
            f++;
        }
        i++;
        ssize_t size = sizeof(feats)/sizeof(float);
       
    }
    fclose(fin);
    
    return *data;
}

int main(int argc, char* argv[]){
  struct KMData data = kmdata_load(argv[1]);
}

问题分析与修复方案

1. features 类型匹配错误

features是float**类型(指向float指针的指针),但当前用malloc((tokens - (2*lines)) * sizeof(float))分配的是连续float数组,仅对应float*类型,完全不符合float**的结构要求。

修复方式:
先分配样本数量对应的指针数组,再为每个指针分配单样本的特征内存:

// 计算单样本特征维度
int dim = (tokens - lines) / lines;
data->dim = dim;
// 分配行指针(对应每个样本)
data->features = malloc(lines * sizeof(float*));
// 为每个样本分配特征内存
for (int i = 0; i < lines; i++) {
    data->features[i] = malloc(dim * sizeof(float));
}

另外,你打印的sizeof(*(data->features))得到8,是因为64位系统中float*指针本身占8字节,这个值和你分配的数组内存大小无关,该打印没有意义。

2. ndata 未初始化导致越界

结构体中的ndata未初始化,初始值是随机垃圾值。第一次执行data->ndata++后,data->ndata-1可能是一个极大的数,直接访问data->labels[data->ndata-1]会触发数组越界,导致段错误。

修复方式:
分配结构体后立即初始化ndata为0:

struct KMData *data = malloc(sizeof(struct KMData));
data->ndata = 0; // 初始化至关重要
data->labels = malloc(lines * sizeof(int));

3. 冗余的内存分配操作

一开始尝试将结构体、labels、features的内存一次性分配:

struct KMData *data = malloc(sizeof(struct KMData) + (lines * sizeof(int)) + ((tokens - (2*lines)) * sizeof(float)));

但随后又单独为labels和features调用malloc,导致之前分配的额外内存被浪费,逻辑混乱。建议分开分配结构体和内部数组,避免混合操作。

4. strtok 逻辑错误

读取特征时,先调用token = strtok(NULL, " \t");,进入循环又调用一次token = strtok(NULL, " \t");,会跳过第一个特征值。

修复方式:
调整strtok调用逻辑,确保读取所有特征:

token = strtok(line, " \t");
data->labels[data->ndata-1] = atoi(token);

int f = 0;
// 直接读取第一个特征
token = strtok(NULL, " \t");
while (token != NULL) {
    data->features[data->ndata-1][f] = atof(token);
    if (i == 0) {
        printf("token %d: %f\n", f, data->features[data->ndata-1][f]);
    }
    f++;
    token = strtok(NULL, " \t");
}

5. 内存泄漏风险

当前函数返回结构体副本(return *data;),但原结构体指针data未被释放,会导致内存泄漏。建议修改函数返回struct KMData*指针,使用后手动释放内存:

// 修改函数返回类型
struct KMData* kmdata_load(char *datafile) {
    // ... 原有逻辑 ...
    return data;
}

// main函数中使用
struct KMData *data = kmdata_load(argv[1]);
// 使用完毕后释放内存
for (int i = 0; i < data->ndata; i++) {
    free(data->features[i]);
}
free(data->features);
free(data->labels);
free(data);

内容的提问来源于stack exchange,提问作者Daisuke Oto

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 14:19:55