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

C语言链表实现中Valgrind显示malloc次数比实际多1次的原因

双向链表Valgrind显示分配次数比实际多1次的原因

我用C实现双向链表,只在push_int_list函数里调用malloc,代码中调用了5次这个函数,但Valgrind显示执行了6次分配与释放。调整push_int_list的调用次数后,分配次数始终比实际多1次。排除了栈上的Int结构体被计数的可能,请问这是为什么?


代码实现

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

// --------------------------------------------------------------------------------
// Linked list Struct to containt data

typedef struct int_list
{
    int data;
    struct int_list *next;
    struct int_list *previous;
} int_list;
// --------------------------------------------------------------------------------
// Struct to track linked list data

typedef struct
{
    size_t active_length;
    struct int_list *head;
    struct int_list *tail;
} Int;
// --------------------------------------------------------------------------------
// Basic initialization of data tracking struct

void init_int_list(Int *list) {
    list->active_length = 0;
    list->head = NULL;
    list->tail = NULL;
}
// --------------------------------------------------------------------------------
// Function that allows user to push a struct to any indice

int push_int_list(Int *list, int data, size_t index) {
    if (index < 0 || index > list->active_length) {
        fprintf(stderr, "INdex out of range\n");
        return -1;
    }
    struct int_list *dat = malloc(sizeof(int_list));
    if (!dat) {
        fprintf(stderr, "malloc failed in file %s at line %d\n", __FILE__, __LINE__);
        return -1;
    }
    if (list->active_length == 0) {
        // populate struct
        dat->data = data;
        dat->previous = NULL;
        dat->next = NULL;

        // Update linked list tracking struct
        list->head = dat;
        list->tail = dat;
        list->active_length += 1;
    }
    else if (index == 0 && list->active_length > 0) {
        // populate struct
        dat->previous = NULL;
        dat->next = list->head;
        dat->data = data;

        // Update data in the struct occupying hte next index
        (list->head)->previous = dat;

        // Update linked list tracking struct
        list->head = dat;
        list->active_length += 1;
    }
    else if (list->active_length > 0 && index == list->active_length) {
        // populate struct
        dat->data = data;
        dat->next = NULL;
        dat->previous = list->tail;

        // Update struct occupying previous index
        (list->tail)->next = dat;

        // Update linked list tracking struct
        list->tail = dat;
        list->active_length += 1;
    }
    // The next two if statements are mean to reduce the iteration time by half
    // if the index to be inserted is between 0 and active_length
    else if (index < list->active_length / 2) {
        struct int_list *current = list->head;
        struct int_list *previous = NULL;
        for (size_t i = 0; i < index; i++) {
            previous = current;
            current = current->next;
        }
        // populate struct
        dat->data = data;
        dat->next = current;
        dat->previous = previous;

        // Update previous and next struct
        previous->next = dat;
        current->previous = dat;

        // Update linked list tracking struct
        list->active_length += 1;
    }
    else {
        struct int_list *current = list->tail;
        struct int_list *next = NULL;
        for (size_t i = list->active_length; i > index; i--) {
            next = current;
            current = current->previous;
        }
        // Update struct
        dat->data = data;
        dat->next = next;
        dat->previous = current;

        // Update previous and next struct
        next->previous = dat;
        current->next = dat;

        // Update linked list tracking struct
        list->active_length += 1;
    }
    return 1;
}

void free_int_list(Int *list) {
    if (list->active_length > 0) {
        struct int_list *tmp = NULL;
        struct int_list *head = list->head;
        while (head->next != NULL) {
            tmp = head;
            head = tmp->next;
            free(tmp);
        }
        free(head);
    }
}

// Begin code
int main() {
    Int list;
    init_int_list(&list);
    push_int_list(&list, 1, 0);
    push_int_list(&list, 2, 1);
    push_int_list(&list, 3, 2);
    push_int_list(&list, 4, 3);
    push_int_list(&list, 8, 0);

    // Print linked list
    struct int_list *current = list.head;
    for (size_t i = 0; i < list.active_length; i++) {
        printf("%d\n", current->data);
        current = current->next;
    }

    free_int_list(&list);
    return 0;
}

Valgrind检测信息

==10074== Memcheck, a memory error detector
==10074== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==10074== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
==10074== Command: ./test
==10074== 
8
1
2
3
4
==10074== 
==10074== HEAP SUMMARY:
==10074==     in use at exit: 0 bytes in 0 blocks
==10074==   total heap usage: 6 allocs, 6 frees, 1,144 bytes allocated
==10074== 
==10074== All heap blocks were freed -- no leaks are possible
==10074== 
==10074== For lists of detected and suppressed errors, rerun with: -s
==10074== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

原因分析

多出来的那次分配并非你的代码导致,而是C标准库内部的内存分配操作。比如你代码中调用的printf函数,在首次执行时,stdio库会分配一个内部缓冲区(用于行缓冲或全缓冲),这个分配会被Valgrind统计进去。

你可以做验证:把代码中的printf输出部分注释掉,重新用Valgrind运行,此时分配次数就会和你调用push_int_list的次数完全一致(5次)。

另外Valgrind已经明确显示All heap blocks were freed -- no leaks are possible,说明所有分配的内存都被正确释放了,你的链表实现没有内存泄漏问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 07:50:27