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

C语言无内存泄漏实现链表:内存丢失与循环链表问题排查

你的链表内存问题根源与修复方案

嘿,我帮你揪出代码里的两个核心错误了,这直接导致了你遇到的内存丢失和循环链表问题:

1. 内存分配的大小完全错了

你写的malloc(sizeof(headPart))是个典型的新手坑——headPart是RailPart*类型的指针,它的大小在32位系统是4字节、64位是8字节,但你的RailPart结构体大小肯定比这个大得多。这就导致你分配的内存远不足以容纳整个结构体,后续给toPush的成员赋值时,会直接越界写到堆上的其他内存区域,把之前节点的nextPart指针给覆盖掉了——这就是为什么加第4个节点后,第3个节点的指针变成异常地址,本质是内存被越界写破坏了。

修复方法:把malloc的参数改成sizeof(RailPart),这样才会分配整个结构体需要的内存:

RailPart *toPush = (RailPart*)malloc(sizeof(RailPart));

2. 局部数组越界,破坏了栈内存

你定义的sPart[1]和ePart[1]只有1个元素的空间,但用sscanf("%[^,],%[^,],...", sPart, ePart,...)的时候,%[^,]会读取到逗号前的所有字符,然后自动在末尾加'\0'字符串结束符。比如输入是"a,b,100,200",sPart会被写入'a',然后'\0'会写到sPart[1]——这个位置已经超出数组范围了,属于栈内存越界。

栈内存越界会乱改栈上的其他变量(比如循环计数器、指针、函数返回地址),这就是为什么你会遇到“未使用内存地址在sscanf后变化”甚至循环链表的情况——栈上的指针被越界写给篡改了。

修复方法:
不用搞数组,直接用单个字符变量配合%c格式符读取就行,彻底避免越界:

char sPart, ePart;
sscanf(partInfo, "%c,%c,%[^,],%[^,]", &sPart, &ePart, len, price);

如果非要用数组,至少把大小改成2(比如char sPart[2];),能容纳字符加结束符,但显然用单个字符更直接。

额外的优化建议

  • 给sPartIdx和ePartIdx初始化默认值(比如-1),避免结构体里残留堆内存的脏数据;
  • 每次调用sscanf和strtol后做个校验,比如检查sscanf的返回值是否为4(成功读取4个字段),strtol的noneDigit是否指向合法的位置,避免非法输入导致的崩溃;
  • 记得程序结束时遍历链表,逐个释放节点内存,避免内存泄漏。

修正后的完整代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_FIELD_SIZE 20
#define BASE_10 10

typedef struct RailPart {
    char start;
    char end;
    int sPartIdx;
    int ePartIdx;
    int len;
    int price;
    struct RailPart *nextPart;
} RailPart;

RailPart *headPart = NULL;

void createRailPart(char *partInfo, char *connectTypes){
    char sPart, ePart;
    char len[MAX_FIELD_SIZE];
    char price[MAX_FIELD_SIZE];
    char *noneDigit;

    // 修正内存分配大小
    RailPart *toPush = (RailPart*)malloc(sizeof(RailPart));
    if (toPush == NULL){
        perror("malloc failed");
        return;
    }

    // 初始化索引为默认值
    toPush->sPartIdx = -1;
    toPush->ePartIdx = -1;

    // 修正sscanf读取方式,校验返回值
    if (sscanf(partInfo, "%c,%c,%[^,],%[^,]", &sPart, &ePart, len, price) != 4) {
        fprintf(stderr, "Invalid partInfo format!\n");
        free(toPush); // 分配失败要及时释放内存
        return;
    }

    toPush->start = sPart;
    toPush->end = ePart;

    for (int i = 0; i < (int) strlen(connectTypes); i++){
        if (toPush->end == connectTypes[i]){
            toPush->ePartIdx = i;
        }
        if (toPush->start == connectTypes[i]){
            toPush->sPartIdx = i;
        }
    }

    // 校验长度转换是否合法
    toPush->len = (int) strtol(len, &noneDigit, BASE_10);
    if (*noneDigit != '\0' && *noneDigit != ',') {
        fprintf(stderr, "Invalid length value!\n");
        free(toPush);
        return;
    }

    // 校验价格转换是否合法
    toPush->price = (int) strtol(price, &noneDigit, BASE_10);
    if (*noneDigit != '\0') {
        fprintf(stderr, "Invalid price value!\n");
        free(toPush);
        return;
    }

    // 插入链表头部
    toPush->nextPart = headPart;
    headPart = toPush;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 15:03:11