为何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
相关产品推荐
相关产品推荐

