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

C语言联合体内存值异常变更:未操作成员为何非空?

问题原因分析

你遇到的问题核心在于union(共用体)的内存共享特性。

在C语言中,union的所有成员会共享同一块内存空间——也就是说,obj->x.string和obj->x.filename其实指向的是内存中的同一个位置。当你在modify_struct里给obj->x.filename分配内存并赋值后,obj->x.string的值也会被覆盖成这个新的内存地址,不再是你初始化时的NULL了。

所以在free_all函数里,你同时检查string和filename是否非空,这两个条件都会成立,相当于尝试两次释放同一块内存(第一次释放string,第二次释放filename,但它们指向的是同一个地址),这就导致了重复释放内存的未定义行为,程序自然会出错。

另外还要提一下你代码里的一个小bug:malloc(strlen(value))只分配了和字符串长度相等的内存,但C语言的字符串需要额外的一个字节来存储终止符'\0',所以这里应该改成malloc(strlen(value) + 1),否则后续的memcpy会没有空间存放终止符,导致字符串操作出现未定义行为。

修复方案

针对你的需求,有两种常见的修复方式:

方案一:把Union改成Struct(最直接)

如果你需要同时独立维护string和filename两个指针,那union并不适合你的场景,应该换成struct(结构体)——结构体的每个成员都有独立的内存空间,不会互相干扰:

// 修改结构体定义
typedef struct my_struct {
    struct {
        char *string;
        char *filename;
    } x;
} my_struct_t;

这样修改后,obj->x.string和obj->x.filename是两个独立的指针,初始化时都设为NULL,修改其中一个不会影响另一个。你的free_all函数可以保持不变,因为现在两个指针是独立的,各自管理自己的内存。

同时记得修复modify_struct里的malloc问题:

void modify_struct(my_struct_t *obj, const char *value){
    // 多分配一个字节存'\0'
    char *temp = malloc(strlen(value) + 1);
    // 用strcpy更方便,自动处理终止符
    strcpy(temp, value);
    obj->x.filename = temp;
}

方案二:保留Union,但添加标记位(如果必须用Union)

如果你确实需要用union(比如要节省内存,同一时间只使用其中一个指针),那需要额外添加一个标记位,记录当前union中哪个成员是有效的,释放时只释放对应的成员:

// 修改结构体,添加标记位
typedef enum {
    TYPE_STRING,
    TYPE_FILENAME,
    TYPE_NONE
} my_type_t;

typedef struct my_struct {
    my_type_t type; // 标记当前使用的是union的哪个成员
    union {
        char *string;
        char *filename;
    } x;
} my_struct_t;

然后修改创建、修改和释放函数:

my_struct_t *build_struct(){
    my_struct_t *obj = malloc(sizeof(my_struct_t));
    obj->type = TYPE_NONE;
    // 初始化union成员为NULL
    obj->x.string = NULL;
    return obj;
}

void modify_struct(my_struct_t *obj, const char *value){
    // 如果之前有已分配的内存,先释放
    if (obj->type == TYPE_FILENAME && obj->x.filename != NULL) {
        free(obj->x.filename);
    } else if (obj->type == TYPE_STRING && obj->x.string != NULL) {
        free(obj->x.string);
    }
    // 分配内存并赋值
    char *temp = malloc(strlen(value) + 1);
    strcpy(temp, value);
    obj->x.filename = temp;
    obj->type = TYPE_FILENAME; // 更新标记位
}

void free_all(my_struct_t *obj){
    // 根据标记位释放对应的成员
    if (obj->type == TYPE_FILENAME && obj->x.filename != NULL) {
        free(obj->x.filename);
    } else if (obj->type == TYPE_STRING && obj->x.string != NULL) {
        free(obj->x.string);
    }
    free(obj);
}

这样就能确保每次只释放当前有效的那个指针,避免重复释放或释放未分配的内存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:54:46