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

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__

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 14:02:48