如何证明释放链表的功能写得不好?

时间:2013-02-02 11:16:32

标签: c linux memory-management linked-list

有关C中链接列表的问题。假设,我们有以下结构,它们定义链表中的节点和写得错误的free_list函数,(在我看来)应该导致错误:

typedef struct node *nodeT;

typedef struct node
{
    int* elem;
    nodeT next;
} *nodeT;

void free_list(nodeT list)
{
    nodeT node;

    for (node = list; node; node = node->next)
    {
        free(node->elem);
        free(node);
    }
}

如上所述,我们将节点定义为 nodeT ,并将一个函数用于释放列表 free_list 。对我来说,明显的错误是里面的 free_list 函数我们没有一个临时指针存储节点值。

然而,当我编译代码(在Linux上),我在其中创建一个包含少量元素的链表时,程序不会崩溃,似乎一切正常。

我的问题是:是否有一种简单的方法可以证明此函数( free_list )写得不好?简单来说,我的意思是设置一些编译器标志(-Wall没有显示任何错误)或者使用Valgrind等工具(与memcheck一起使用),这没有多大帮助)?

更新:根据要求提供测试用例:

int main()
{
    nodeT myType;
    nodeT tmpPtr;

    myType = malloc(sizeof(nodeT));
    myType->item = malloc(sizeof(int));
    *(myType->item) = 0;
    myType->next = malloc(sizeof(nodeT));
    tmpPtr = myType->next;

    tmpPtr->item = malloc(sizeof(int));
    *(tmpPtr->item) = 1;
    tmpPtr->next = malloc(sizeof(nodeT));
    tmpPtr = tmpPtr->next;

    tmpPtr->item = malloc(sizeof(int));
    *(tmpPtr->item) = 2;
    tmpPtr->next = malloc(sizeof(nodeT));
    tmpPtr = tmpPtr->next;

    tmpPtr->item = malloc(sizeof(int));
    *(tmpPtr->item) = 3;
    tmpPtr->next = NULL;

    free_list(myType);

    return 0;
}

这是Valgrind输出:

valgrind --tool=memcheck ./a.out
...
==4318== Invalid read of size 8
==4318==    at 0x400579: free_list (in /home/melon/a.out)
==4318==    by 0x40069E: main (in /home/melon/a.out)
==4318==  Address 0x51f1048 is 0 bytes after a block of size 8 free'd
==4318==    at 0x4C2A82E: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4318==    by 0x400574: free_list (in /home/melon/a.out)
==4318==    by 0x40069E: main (in /home/melon/a.out)
...

3 个答案:

答案 0 :(得分:4)

我不确定你是如何使用valgrind进行测试的,但是使用默认参数它可以很好地检测到这个问题:

==4464== Invalid read of size 8
==4464==    at 0x400571: free_list (list.c:15)
==4464==    by 0x4005DF: main (list.c:30)
==4464==  Address 0x51e0048 is 8 bytes inside a block of size 16 free'd
==4464==    at 0x4C2AD3C: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4464==    by 0x40056C: free_list (list.c:18)
==4464==    by 0x4005DF: main (list.c:30)

我刚刚将其添加到程序中以使其可测试:

int main() {
    nodeT head = malloc(sizeof(struct node));
    nodeT node1 = malloc(sizeof(struct node));
    head->next = node1;
    head->elem = NULL;
    node1->next = NULL;
    node1->elem = NULL;

    free_list(head);

    return 0;
}

答案 1 :(得分:1)

在释放内存之前,您可以memset将节点归零。然后它肯定会崩溃。

答案 2 :(得分:1)

除了代码取消引用指向已释放的内存的指针这一事实之外,您不需要任何其他证据。这是未定义的行为。调用未定义的行为是不好的。

所以有你的证明。