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

为何free()会修改C语言结构体成员的值?

已释放内存访问的未定义行为解析

原测试代码

typedef struct nodeA NodeA;
struct nodeA{
        int data;
        NodeA* next;
};

void main(){
        NodeA* test1 = (NodeA*) malloc(1 * sizeof(NodeA));
        test1->next = (NodeA*) malloc(1 * sizeof(NodeA));
        test1->data = 999;
        printf("NODE: next=%p data=%d\n", test1->next, test1->data);
        printf("NEXT: next=%p data=%d\n", test1->next->next, test1->next->data);
        
        free(test1);
        printf("FREE NODE: next=%p data=%d\n", test1->next, test1->data);
        printf("FREE NEXT: next=%p data=%d\n", test1->next->next, test1->next->data);
}

运行输出

NODE: next=0x563bd8335690 data=999
NEXT: next=(nil) data=0
FREE NODE: next=0x563bd8335010 data=0
FREE NEXT: next=(nil) data=1

问题解析

1. 释放后test1节点成员值变化的原因

free(test1)执行后,test1指向的内存块被归还给内存分配器。内存分配器通常会修改这块内存的内容,用来维护空闲内存链表的元数据——比如标记当前块的大小、记录下一个空闲块的地址。所以你看到的test1->next地址变化、test1->data变成0,本质是内存分配器写入的内部管理数据,不是原来的节点值了。

2. 下一个节点data从0变1的原因

这里要注意:test1被释放后,test1->next这个指针本身已经存放在已释放的内存里,是无效的。你通过test1->next去访问下一个节点时,首先读出来的是内存分配器修改后的错误地址(0x563bd8335010),然后去读取这个地址的data值。这个地址可能是分配器刚标记的空闲块,或是进程地址空间里的其他随机内存,里面的数值完全是巧合,没有任何逻辑可言。

3. 改为char*触发段错误的本质

这依然是未定义行为的表现,没有绝对的固定规律,但有概率性的趋势:

  • 当data是char*类型时,你访问的是已释放内存里的指针值,这个指针可能指向了进程无权访问的内存区域(比如内核空间、未映射的物理内存),此时操作系统会触发页错误,也就是段错误;
  • 而int类型的data是直接读取内存中的数值,只要该地址还在进程的地址空间范围内,就不会触发段错误,只会读出随机的垃圾值;
  • 这些行为取决于内存分配器的实现、当前进程的内存布局、操作系统的内存管理策略,对用户程序来说完全不可预测,也绝对不能依赖。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 09:27:27