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

链表内存释放触发ntdll!RtlIsZeroMemory错误c0000374求助

链表内存释放函数导致堆损坏错误的问题分析与修复

问题场景

实现链表节点内存申请后,编写释放函数时程序崩溃,调试提示堆损坏错误(c0000374),即使释放前检查了节点不为NULL。

相关代码

test.h

typedef struct snPng_block {
    uint8_t size[4];
    size_t  dataSize;
    uint8_t *data;
    uint8_t crc[4];
    struct snPng_block *next;
} snPng_block;

int snPng_new(snPng_block **obj, size_t length)
{
    if(!length) {
        return snErr_ErrNullData;
    }
    snPng_block *head = NULL;
    snPng_block *ptr = NULL;
    size_t index;

    for(index = 0; index < length; ++index) {
        if(head == NULL) {
            if(!(head = (snPng_block *)malloc(sizeof(snPng_block)))) {
                return snErr_Memory;
            }
            ptr = head;
        } else {
            if(!(ptr->next = (snPng_block *)malloc(sizeof(snPng_block)))) {
                return snErr_Memory;
            }
            ptr = ptr->next;
        }
    }

    ptr->next = NULL;
    (*obj) = head;

    return snErr_OK;
}

int snPng_release(snPng_block **obj)
{
    snPng_block *ptr = NULL;
    for(ptr = (*obj); ptr; ptr = ptr->next) {
        free(ptr);
    }

    return snErr_OK;
}

test.c

int main(int argc, char **argv)
{
    snPng_block *block = NULL;
    snPng_new(&block, 9);
    snPng_block *ptr = NULL;
    snSize index;

    for(index = 0, ptr = block; ptr != NULL; ++index, ptr = ptr->next) {
        printf("%"PRIu64" node: %p\n", index + 1, ptr);
    }
    snPng_release(&block);
    return 0;
}

GDB调试结果

Reading symbols from #output\main.exe...
(gdb) r
Starting program: E:\Projects\Shark_Coast\#output\main.exe
[New Thread 13724.0x8ac]
1 node: 00000244171955F0
2 node: 0000024417195830
3 node: 0000024417195890
4 node: 000002441719ADA0
5 node: 000002441719AE00
6 node: 000002441719AE60
7 node: 000002441719AEC0
8 node: 000002441719AF20
9 node: 00000244171994F0
warning: Critical error detected c0000374

Thread 1 received signal SIGTRAP, Trace/breakpoint trap.
0x00007fff7ab2f633 in ntdll!RtlIsZeroMemory () from C:\WINDOWS\SYSTEM32\ntdll.dll

(gdb) backtrace
#0  0x00007fff7ab2f633 in ntdll!RtlIsZeroMemory () from C:\WINDOWS\SYSTEM32\ntdll.dll
#1  0x00007fff7ab383f2 in ntdll!RtlpNtSetValueKey () from C:\WINDOWS\SYSTEM32\ntdll.dll
#2  0x00007fff7ab386da in ntdll!RtlpNtSetValueKey () from C:\WINDOWS\SYSTEM32\ntdll.dll
#3  0x00007fff7ab3e361 in ntdll!RtlpNtSetValueKey () from C:\WINDOWS\SYSTEM32\ntdll.dll
#4  0x00007fff7aa55b43 in ntdll!RtlGetCurrentServiceSessionId () from C:\WINDOWS\SYSTEM32\ntdll.dll
#5  0x00007fff7aa547b1 in ntdll!RtlFreeHeap () from C:\WINDOWS\SYSTEM32\ntdll.dll
#6  0x00007fff781bf05b in ucrtbase!_free_base () from C:\WINDOWS\System32\ucrtbase.dll
#7  0x00007ff7f3653480 in snPng_release (obj=<optimized out>) at includes/image/test.h:53
#8  main (argc=<optimized out>, argv=<optimized out>) at test\test.c:29

错误原因

snPng_release函数的循环逻辑存在致命问题:

  • 执行free(ptr)后,ptr指向的内存已被回收,成为野指针
  • 后续循环中尝试访问ptr->next,本质是访问已释放的内存,破坏了堆结构,触发系统内存保护错误(c0000374)

修复方案

修正snPng_release函数

释放当前节点前,先保存下一个节点的地址,避免访问已释放内存:

int snPng_release(snPng_block **obj)
{
    if (!obj || !(*obj)) {
        return snErr_OK;
    }
    
    snPng_block *current = *obj;
    snPng_block *next_node = NULL;
    
    while (current != NULL) {
        next_node = current->next; // 提前保存下一个节点
        free(current);             // 释放当前节点
        current = next_node;       // 切换到下一个节点
    }
    
    *obj = NULL; // 将原指针置空,避免后续误用野指针
    return snErr_OK;
}

修复snPng_new的内存泄漏问题

原snPng_new如果在循环中malloc失败,已申请的节点不会被释放,造成内存泄漏。补充失败时的清理逻辑:

int snPng_new(snPng_block **obj, size_t length)
{
    if(!length) {
        return snErr_ErrNullData;
    }
    snPng_block *head = NULL;
    snPng_block *ptr = NULL;
    size_t index;

    for(index = 0; index < length; ++index) {
        snPng_block *new_node = (snPng_block *)malloc(sizeof(snPng_block));
        if (!new_node) {
            // 申请失败,释放已创建的所有节点
            snPng_release(&head);
            return snErr_Memory;
        }
        
        if(head == NULL) {
            head = new_node;
            ptr = head;
        } else {
            ptr->next = new_node;
            ptr = ptr->next;
        }
    }

    if (ptr) {
        ptr->next = NULL;
    }
    *obj = head;

    return snErr_OK;
}

内容的提问来源于stack exchange,提问作者S-N

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 02:12:40