1

关于 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);
    }
}

正如你在上面看到的,我们已经将 node 定义为nodeT和一个释放列表free_list的函数。对我来说,明显的错误是在free_list函数里面for我们没有一个临时指针来存储节点值。

但是,当我编译代码(在 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)
...
4

3 回答 3

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;
}
于 2013-02-02T11:25:29.000 回答
1

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

于 2013-02-02T11:18:54.677 回答
1

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

所以有你的证据。

于 2013-02-02T11:21:29.173 回答