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

C语言函数内通过结构体指针赋值 返回main时填充内容丢失

问题根因

返回main函数后结构体内容丢失,核心是几个C语言值传递和内存分配的典型错误:

  • C语言函数参数是值传递,你在carica函数内部给形参txt做malloc赋值,只会修改函数栈上的txt副本,main里定义的testo* txt始终是未初始化的野指针,根本拿不到函数内分配的结构体地址。
  • txt->f的内存分配逻辑错误:f是char**类型,本质是存储n个字符串指针的数组,不需要直接分配n*BUFSIZ长度的字符空间,当前写法会造成后续指针访问越界。
  • 字符串内存分配没有预留'\0'结束符的位置:strlen返回的长度不包含字符串结束符,malloc(strlen(aux))会少分配1字节,strcpy执行时会出现堆越界写入。
  • 清空aux数组的循环逻辑是未定义行为:第一次进入循环时aux是栈上未初始化的内存,判断aux[j]!='\0'的结果不可预期,实际上fgets会自动在读取内容末尾加结束符,不需要手动逐字节清空数组。
修正方案

如果要保留「在函数内部完成结构体内存分配」的逻辑,最直接的改法是把结构体参数改成二级指针,传入main中txt指针的地址,让函数可以修改外部指针的指向。
修正后的可运行代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct{
    char** f;
    int n;
}testo;

int carica(char*, testo**);
void mostra(testo*);

int main(int argc, char *argv[]) {
    testo* txt;
    int ret = carica("prova.txt", &txt);
    if(ret != 0){
        printf("加载文件失败,错误码:%d\n", ret);
        return 1;
    }
    mostra(txt);
    // 用完手动释放内存,避免泄漏
    for(int i=0; i<txt->n; i++){
        free(txt->f[i]);
    }
    free(txt->f);
    free(txt);
    return 0;
}

int carica(char* nf, testo** txt){
    FILE* file;
    if( (file = fopen(nf,"r") )==NULL){
        return -1;
    }
    int n_of_strings;
    if(fscanf(file,"%d",&n_of_strings)==EOF || n_of_strings<1){
        fclose(file);
        return -2;
    }
    // 读走fscanf读取数字后残留的换行符
    getc(file);

    *txt = malloc(sizeof(testo));
    if (*txt==NULL){
        fclose(file);
        return -3;
    }
    (*txt)->n = n_of_strings;
    // 给指针数组分配空间,每个元素是char*类型
    (*txt)->f = malloc(sizeof(char*) * n_of_strings);
    if((*txt)->f == NULL){
        free(*txt);
        fclose(file);
        return -3;
    }

    char aux[BUFSIZ];
    for(int i=0; i<n_of_strings; i++){
        if(fgets(aux, BUFSIZ, file) == NULL){
            // 处理文件行数不足的异常
            for(int j=0; j<i; j++) free((*txt)->f[j]);
            free((*txt)->f);
            free(*txt);
            fclose(file);
            return -2;
        }
        // 去掉fgets读入的行尾换行符
        size_t len = strlen(aux);
        if(len>0 && aux[len-1] == '\n'){
            aux[len-1] = '\0';
            len--;
        }
        // 多分配1字节存字符串结束符
        (*txt)->f[i] = malloc(len + 1);
        if ((*txt)->f[i]==NULL){
            for(int j=0; j<i; j++) free((*txt)->f[j]);
            free((*txt)->f);
            free(*txt);
            fclose(file);
            return -4;
        }
        strcpy((*txt)->f[i], aux);
    }
    fclose(file);
    return 0;
}

// 测试用打印函数
void mostra(testo* t){
    for(int i=0; i<t->n; i++){
        printf("%d: %s\n", i, t->f[i]);
    }
}
额外注意事项
  • 所有错误分支都要提前关闭文件、释放已分配的内存,避免出现内存泄漏、文件句柄泄漏。
  • 如果不想修改函数签名,也可以在main函数中提前定义栈上的结构体变量、或者提前给testo* txt分配好结构体内存,再把指针传入carica函数直接填充成员即可,不需要在函数内给结构体本身分配空间。

内容的提问来源于stack exchange,提问作者Davide Marchesi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 12:03:23