C语言返回含数组成员的结构体后,成员指针地址不符问题咨询
C语言结构体返回后指针成员与数组成员地址不一致的问题分析与解决
问题现象
以下代码中,init_lexer_fp函数内将结构体的指针成员i赋值为数组成员src的地址,但调用函数返回结构体后,lex.i与lex.src指向不同地址(例如输出:i, src = 0x7ffe20e51840, 0x7ffe20e520a0)。将l.i = l.src移到初始化函数外则正常工作。
typedef struct { char src[2048], *i; FILE *fp; }lexer_fp; lexer_fp init_lexer_fp(FILE *fp) { lexer_fp l; l.fp = fp; fread(l.src, 1, 2048, fp); l.i = l.src; return l; } void decompile(const char* filename) { FILE * fp = fopen(filename, "r"); lexer_fp lex = init_lexer_fp(fp); printf("i, src = %p, %p\n", lex.i, lex.src); fclose(fp); }
原因分析
这不是本地机器独有的问题,而是C语言结构体值返回机制导致的固有问题:
init_lexer_fp中的l是函数栈帧上的局部结构体变量,l.i = l.src让i指向的是这个局部结构体内部src数组的栈地址。- 当函数返回时,编译器会把局部结构体
l的所有内容拷贝到调用者栈帧中的lex变量里。此时lex.src是调用者栈上的新数组空间,但lex.i仍然保留着原来局部变量l的src地址(该地址对应的栈空间已经被销毁,属于无效内存),因此两者地址完全不同,且lex.i指向的是已失效的内存,属于未定义行为。
可行解决方案
方案1:返回结构体指针(动态分配)
通过malloc在堆上分配结构体,避免栈上局部变量销毁导致的指针失效:
typedef struct { char src[2048], *i; FILE *fp; }lexer_fp; lexer_fp* init_lexer_fp(FILE *fp) { lexer_fp *l = malloc(sizeof(lexer_fp)); if (!l) return NULL; // 检查内存分配是否成功 l->fp = fp; fread(l->src, 1, 2048, fp); l->i = l->src; return l; } void decompile(const char* filename) { FILE * fp = fopen(filename, "r"); if (!fp) return; // 检查文件打开是否成功 lexer_fp *lex = init_lexer_fp(fp); if (!lex) { fclose(fp); return; } printf("i, src = %p, %p\n", lex->i, lex->src); fclose(fp); free(lex); // 记得释放堆内存 }
方案2:初始化函数外赋值指针
这是你已经验证有效的方式,返回结构体后再让i指向当前结构体的src:
void decompile(const char* filename) { FILE * fp = fopen(filename, "r"); lexer_fp lex = init_lexer_fp(fp); lex.i = lex.src; // 移到此处赋值 printf("i, src = %p, %p\n", lex.i, lex.src); fclose(fp); }
方案3:将数组成员改为动态分配的指针
把结构体中的src从数组改为指针,在初始化函数中动态分配缓冲区:
typedef struct { char *src, *i; FILE *fp; }lexer_fp; lexer_fp init_lexer_fp(FILE *fp) { lexer_fp l; l.fp = fp; l.src = malloc(2048); if (!l.src) { l.i = NULL; return l; } fread(l.src, 1, 2048, fp); l.i = l.src; return l; } void decompile(const char* filename) { FILE * fp = fopen(filename, "r"); lexer_fp lex = init_lexer_fp(fp); printf("i, src = %p, %p\n", lex.i, lex.src); fclose(fp); free(lex.src); // 释放缓冲区 }
内容的提问来源于stack exchange,提问作者Unknown
相关产品推荐
相关产品推荐

