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

Time:10-27

I'm implementing a circularly linked list and am attempting to make the functions that will free the linked list and the nodes in the linked list. I have a LL_t type that points to the head and tail of the linked list. And then a linked list of LL_node_t nodes.

I have done my best to get the important parts of the code and the output that I am working with to better show my question.

My question is, why in my temp_llnode_ptr start with this value temp_llnode_ptr: 0x7fffcf9c7310, but after calling my free function end up with NEW temp_llnode_ptr: 0x7fffcf9c6010. It starts with the address of my second node. I attempt to free my first node. Then after the function my temp_llnode_ptr has a different value.

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)

CodePudding user response:

Let X be the thing that (*list)->head points to. Then temp_llnode_ptr is set to the address of X->next. Calling free_LL_node(&(*list)->head); frees X. Once X is freed, the contents of its memory are no longer reliable. Among other things, they may well have been changed by the memory management routines, using that memory for its own purposes. X->next is in that memory, so its contents may have changed. Printing (*temp_llnode_ptr) attempts to print the contents of X->next.

Since temp_llnode_ptr points to an object which has been freed, its value is indeterminate, per the C standard. (This is correct, as specified by the C standard, after free(p), the value of p is no longer determined. Even though free cannot change the memory used to hold p, its “value” in a semantic sense may be tied to other data in memory, and that data may be altered by p, so that its “value” is no longer determined.) Additionally, *temp_llnode_ptr attempts to use that memory, and the behavior of that is not defined by the C standard.

CodePudding user response:

Let's look at the problematic part:

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

The first line sets temp_llnode_ptr to point to the next variable in the first node.

The second line looks at the value in that variable (the value which is the address of the second node) and prints it.

The third line frees the first node.

The second line looks at the value in that variable - the variable which is part of the first node - which we just freed - and prints it. Because we just freed this variable we're not supposed to use it any more. In this case it looks like the memory management system has used that spot in memory to hold some pointer related to memory management. That doesn't matter though - that's not your problem - your problem is that you're not supposed to use it at all after you free it.

You seem to think that temp_llnode_ptr points to the next node. I think that works for your purpose, so you can do that. temp_llnode_ptr can be just a pointer (instead of a pointer-to-a-pointer a.k.a. double pointer) and then you have a bunch of extra &s and *s to delete as well.
If you do temp_llnode_ptr = (*list)->head->next; (delete an &) then temp_llnode_ptr will point to the next node (instead of pointing to next which points to the next node) and you should be able to work out the rest yourself.

  • Related