为什么C语言中的链表实现显示的malloc比实际发生的多一个

iszxjhcz  于 2022-12-02  发布在  其他
关注(0)|答案(1)|浏览(122)

这可能是一个非常简单的问题,但是我在互联网上找不到答案。我正在用C语言实现一个链表,并且正在用valgrind检查内存使用和释放。我创建了下面的实现,它工作正常。malloc唯一被调用的地方是在push_int_list函数中。它允许用户创建一个结构体并将其插入到链表中的任何索引中。是的,我知道它不像数组那样是真正的索引,但我使用同样的行话来描述数据点的位置。此外,push_int_list函数将从链表的头部或尾部开始迭代,这取决于结构体将被插入的位置。
下面显示的实现工作正常;但是,当我用valgrind检查内存分配和释放时,显示我执行了6个allocs和6个frees。正如您所看到的,我只调用了push_int_list函数5次,因此应该只执行5个allocs和5个frees。如果我更改push语句的数量,在valgrind中分配和释放的数量总是比我实际执行的多一个。为什么valgrind总是比我实际执行的多一个malloc。

#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)

我想知道它是否将Int结构的示例化作为malloc进行跟踪,但这是在堆栈内存中作为静态分配创建的,所以我不明白为什么这会作为valgrind中的malloc进行跟踪!

toe95027

toe950271#

你想太多了。如果Valgrind说“不可能有泄密”,那么你应该相信它。
内存是为您调用printf而分配的。由于此内存是以“singleton”方式分配的,因此glibc在Valgrind外部运行时不会释放此内存。您应该能够通过使用gdb并在mallocfree上设置断点来确认这一点。glibc中的其他几个函数分配singleton缓冲区。
Valgrind包含了一个释放内存的方法。当guest启动时,它会添加一个重定向到exit()以调用__libc_freeres(),然后退出()。freeres函数通常不会被调用,只存在于像Valgrind这样的工具中。
如果您使用--run-libc-freeres=no运行Valgrind,则memcheck将不再调用__libc_freeres,因此将此分配“检测”为“可达”泄漏。

相关问题