C语言结构体指针数组成员返回垃圾值/0值问题排查修复
问题原因
你遇到的返回后数据错乱核心是悬空指针触发的未定义行为,附带多个内存相关错误,具体如下:
- 核心错误:你用
static修饰结构体inS,只能保证结构体变量本身的内存(包括sptr指针变量、ns成员)是静态存储、不会随函数栈帧销毁,但后续指针赋值逻辑完全错误:先执行inS.sptr = (float *) malloc(n * sizeof(float));分配了堆内存,下一行直接写inS.sptr = fvls;,把sptr存储的地址改成了栈上局部数组fvls的地址,之前malloc的堆内存直接泄漏。而fvls是函数栈帧上的局部变量,函数返回后栈帧被回收,这块内存会被后续函数调用的栈数据覆盖,访问它属于未定义行为。
为什么函数内部打印是正常的?因为函数内打印逻辑在返回前执行,此时栈帧还没销毁,
fvls的内容还没被覆盖,所以能读到正确值;等函数返回、main函数执行后续printf等逻辑时,这块栈内存已经被改写,自然读到0、NaN这类垃圾值。
- 额外错误1:main函数中存在无意义的内存泄漏。你一开始给
inpS、outS以及其sptr成员malloc了内存,紧接着直接把inpS指针赋值为getInputS返回的静态结构体地址,之前malloc的所有内存都没有被释放、也没有被使用,全部泄漏。 - 额外错误2:malloc传参错误。你写的
malloc(MAX_IN * sizeof(inpS->sptr))中,inpS->sptr是float类型指针,在64位系统上占8字节,不是float类型本身的4字节,分配的内存大小不符合预期。 - 额外错误3:缺少边界校验。读取文件时没有判断读取的浮点数个数是否超过
MAX_IN的上限,如果文件内容超过10个浮点数,会直接写爆fvls栈数组,造成栈溢出。 - 额外错误4:错误处理缺失。文件打开失败时你只打印了错误日志,没有中断逻辑,后续会使用未初始化的
n值做内存操作,触发更严重的问题。
修复方法
针对以上问题逐点修正即可:
- 不要让结构体的指针成员指向栈上局部数组,malloc完堆内存后,用
memcpy或者循环把fvls里读取到的浮点数拷贝到堆内存中,不要直接修改指针指向。 - 删除main函数中提前给
inpS、outSmalloc的无意义代码,直接接收getInputS的返回值即可,未使用的多余变量直接删除。 - 所有malloc计算大小时,以指针指向的目标类型大小为准,不要直接取指针本身的大小。
- 读取文件时加边界判断,读取个数达到
MAX_IN时停止读取,避免栈溢出。 - 补全文件打开失败的兜底逻辑,打开失败直接返回NULL,调用方判断返回值后再做后续操作。
- 程序退出前手动free所有malloc的堆内存,避免内存泄漏。
修复后完整代码
#include <stdlib.h> #include <stdint.h> #include <math.h> #include <stdio.h> #include <string.h> #define MAX_IN 10 typedef struct S_t S_t; struct S_t { float *sptr; uint32_t ns; }; S_t *getInputS(char *sdfile) { FILE *inSFP; float fvls[MAX_IN]; static S_t inS; int n; inSFP = fopen(sdfile, "r"); if (inSFP == NULL) { printf("\nFailed to open input file...!!!\n"); return NULL; // 打开失败直接返回空 } n = 0; // 加边界判断,不超过数组最大长度 while (n < MAX_IN && fscanf(inSFP, "%f", &fvls[n]) != EOF) { printf("fvls[%d] = %f\n", n, fvls[n]); n++; } printf("\nScanned all inputs....\n"); inS.ns = (uint32_t) n; // 之前如果sptr指向过已分配内存,先释放避免泄漏 if (inS.sptr != NULL) { free(inS.sptr); } inS.sptr = (float *) malloc(n * sizeof(float)); // 把栈上的数据拷贝到堆内存,不要直接改指针指向 memcpy(inS.sptr, fvls, n * sizeof(float)); for(int i = 0; i < n; i++) printf("inS.sptr[%d] = %f\n", i, inS.sptr[i]); printf("\nInput read from file %s....\n", sdfile); fclose(inSFP); printf("\nClosed file..."); return &inS; } int main(int argc, char *argv[]) { S_t *inpS; static uint32_t n; if (argc < 3) { printf("Usage: %s <input file> <output file>\n", argv[0]); return 1; } char *inFN = argv[1]; inpS = getInputS(inFN); if (inpS == NULL) { return 1; } printf("\nContent from main : \n"); n = inpS->ns; for(int i = 0; i < n; i++) { printf("%f\t", *(inpS->sptr + i)); } printf("\nS structure updated (ns = %d)....\n", n); // 释放堆内存 free(inpS->sptr); inpS->sptr = NULL; return 0; }
内容的提问来源于stack exchange,提问作者skrowten_hermit
相关产品推荐
相关产品推荐

