C语言中free后仍能访问内存?自定义链表pop函数异常排查
问题分析与解决方案
为什么free后还能读出值?
free()根本不是“删除”内存里的数据,它只是告诉操作系统:这块内存我不用了,你可以收回去分给别的程序用。在操作系统还没重新分配这块内存之前,原来的数据大概率还留在原地——你能打印出38只是碰巧,这属于C语言里的未定义行为。段错误不是必然触发的,只有当你访问的内存被操作系统彻底回收、或者被其他数据覆盖时,才会崩。
你的代码里的几个硬伤
1. 非法访问野指针
在pop_item里,你free(list->feet)之后,list->feet就成了野指针(指向已经被释放的内存),这时候再去读list->feet->item完全是违规操作。哪怕这次没崩,下次说不定就出问题,或者输出乱码。正确的做法是先把要打印的值存下来,再free:
void pop_item(ListNode *list) { ListNode *pre = list->feet->back; int last_val = list->feet->item; // 先把值存到栈上 free(list->feet); printf("%i\n", last_val); // 打印栈上的安全值 pre->next = NULL; list->feet = pre; }
2. 结构体设计完全不合理
你给每个ListNode都加了feet指针,这纯粹是浪费内存还埋坑:
- 每个节点都存尾指针,链表越长,内存浪费越严重;
- 你的
push_item只更新了当前尾节点的next,然后把传入的list的feet移到新节点,但其他节点的feet指针还是指向旧的尾节点——这意味着除了第一个节点(你当作链表头的那个),其他所有节点的feet都是错的,以后要是用其他节点的feet操作链表,绝对出问题。
正确的设计是拆分结构体:用一个单独的链表头来管理整个链表的元数据,节点只存数据和前后指针:
// 单个节点的结构体,只存必要数据 typedef struct ListNode { int item; struct ListNode *back; struct ListNode *next; } ListNode; // 链表头,负责管理整个链表的头、尾、长度等信息 typedef struct LinkedList { ListNode *head; ListNode *tail; // 只在这里存尾指针就够了 int size; // 可选,记录链表长度,方便快速获取 } LinkedList;
这样操作链表时,只需要通过LinkedList结构体来访问头和尾,每个节点不需要额外存尾指针,既省内存,又不会出现多个尾指针不一致的问题。
3. 没处理边界情况
如果链表只有一个节点,list->feet->back就是NULL,这时候pre会变成空指针,后续执行pre->next = NULL直接触发段错误。你得在函数开头加判断,比如:
void pop_item(LinkedList *list) { if (list->head == NULL) { printf("链表为空,无法删除\n"); return; } // 后续逻辑... }
内容的提问来源于stack exchange,提问作者Hasty__
相关产品推荐
相关产品推荐

