为什么调用 free() 会更改不同指针的内存地址?

Why is calling free() changing the memory addresses of a different pointer?

我正在实现一个循环链表,并试图创建将释放链表和链表中节点的函数。我有一个指向链表头部和尾部的 LL_t 类型。然后是 LL_node_t 个节点的链表。

我已尽最大努力获取代码的重要部分和我正在使用的输出,以更好地展示我的问题。

我的问题是,为什么在我的 temp_llnode_ptr 中以这个值 temp_llnode_ptr: 0x7fffcf9c7310 开始,但在调用我的自由函数后以 NEW temp_llnode_ptr: 0x7fffcf9c6010 结束。它以我的第二个节点的地址开头。我尝试释放我的第一个节点。然后在函数之后我的 temp_llnode_ptr 有一个不同的值。

void 
free_LL (LL_t** list)
{
    LL_node_t** temp_llnode_ptr = NULL;
    
    for (int i = 0; i < (*list)->LL_length; i++)
    {
        printf("Inside FOR loop\n");

        printf("list->head: %p\n", (void*)(*list)->head);
        printf("list->tail: %p\n", (void*)(*list)->tail);

        temp_llnode_ptr = &(*list)->head->next;
        printf("temp_llnode_ptr: %p\n", (void*)(*temp_llnode_ptr));
        printf("(*temp_llnode_ptr)->next: %p\n", (void*)(*temp_llnode_ptr)->next);


        free_LL_node(&(*list)->head);
        printf("NEW temp_llnode_ptr: %p\n", (void*)(*temp_llnode_ptr));
        printf("NEW list->head: %p\n", (void*)(*list)->head);

    }
}

void 
free_LL_node (LL_node_t** node)
{
    (*node)->next = NULL;
    (*node)->data = NULL;
    printf("node about to be FREED is: %p\n", (void*)(*node));
    free(*node);
    *node = NULL;
}

OUTPUT FROM PRINT FUNC (The nodes in my linked list)
Node#: 0 | Current node: 0x7fffcf9c72f0 | Node data: 10 | Next node 0x7fffcf9c7310
Node#: 1 | Current node: 0x7fffcf9c7310 | Node data: 20 | Next node 0x7fffcf9c7330
Node#: 2 | Current node: 0x7fffcf9c7330 | Node data: 30 | Next node 0x7fffcf9c72f0

Inside FOR loop
list->head: 0x7fffcf9c72f0
list->tail: 0x7fffcf9c7330
temp_llnode_ptr: 0x7fffcf9c7310   <--- Why do these change?
(*temp_llnode_ptr)->next: 0x7fffcf9c7330
node about to be FREED is: 0x7fffcf9c72f0
NEW temp_llnode_ptr: 0x7fffcf9c6010   <--- Why do these change?
NEW list->head: (nil)

X成为(*list)->head指向的东西。然后temp_llnode_ptr设置为X->next的地址。调用 free_LL_node(&(*list)->head); 释放 X。一旦 X 被释放,其内存中的内容将不再可靠。除其他事项外,它们很可能已被内存管理例程更改,将内存用于自己的目的。 X->next 在那个内存中,所以它的内容可能已经改变了。打印 (*temp_llnode_ptr) 尝试打印 X->next.

的内容

由于 temp_llnode_ptr 指向一个已被释放的对象,根据 C 标准,它的值是不确定的。 (这是正确的,按照C标准的规定,在free(p)之后,p不再确定。即使free不能更改用于保存 p 的内存,其语义意义上的“值”可能与内存中的其他数据相关联,并且该数据可能会被 p 更改,因此其“值”为 no不再确定。)此外,*temp_llnode_ptr 尝试使用该内存,并且其行为未由 C 标准定义。

让我们看看有问题的部分:

    temp_llnode_ptr = &(*list)->head->next;
    printf("temp_llnode_ptr: %p\n", (void*)(*temp_llnode_ptr));
    free_LL_node(&(*list)->head);
    printf("NEW temp_llnode_ptr: %p\n", (void*)(*temp_llnode_ptr));

第一行设置temp_llnode_ptr指向第一个节点中的next变量。

第二行查看该变量中的值(该值是第二个节点的地址)并打印它。

第三行释放第一个节点。

第二行查看该变量中的值——该变量是我们刚刚释放的第一个节点的一部分——并打印它。因为我们刚刚释放了这个变量,所以我们不应该再使用它了。在这种情况下,内存管理系统似乎已经使用内存中的那个位置来保存一些与内存管理相关的指针。不过那没关系 - 那不是你的问题 - 你的问题是你不应该在释放它后 根本 使用它。

你似乎认为temp_llnode_ptr指向下一个节点。我认为这对您的目的有用,所以您可以这样做。 temp_llnode_ptr 可以只是一个指针(而不是指向指针的指针 a.k.a。双指针)然后你有一堆额外的 &* s也要删除。
如果你做 temp_llnode_ptr = (*list)->head->next; (删除一个 &)然后 temp_llnode_ptr 将指向下一个节点(而不是指向 next 指向下一个节点)你应该是剩下的可以自己解决。