1

The following code in C is the beginning of a linked list project, where memory is allocated for a node (malloc), and values are assigned to the two struct members, i.e. data (int) and pointer to next node:

typedef struct nodeA NodeA;
struct nodeA{
        int data;
        NodeA* next;
};

void main(){
        NodeA* test1 = (NodeA*) malloc(1 * sizeof(NodeA));
        test1->next = (NodeA*) malloc(1 * sizeof(NodeA));
        test1->data = 999;
        printf("NODE: next=%p data=%d\n", test1->next, test1->data);
        printf("NEXT: next=%p data=%d\n", test1->next->next, test1->next->data);
        
        free(test1);
        printf("FREE NODE: next=%p data=%d\n", test1->next, test1->data);
        printf("FREE NEXT: next=%p data=%d\n", test1->next->next, test1->next->data);
}

The results of the two first printf() make sense.
However, after freeing the node (struct) pointer, the values of the members (of this node and of the next one) are modified:

NODE: next=0x563bd8335690 data=999
NEXT: next=(nil) data=0
FREE NODE: next=0x563bd8335010 data=0
FREE NEXT: next=(nil) data=1

Why is that so?
Especially, why does the data of the next node becomes 1, after the free(), instead of remaining 0?

Thanks a lot!

Edit: Thank you for your answers.
OK, using a freed pointer leads to an undefined behavior.
However, I observe that if you define "data" as a char* (instead of an int), then the last print generates a Segmentation fault.
Is there anything to understand about this, or is this just some random and meaning less behavior?

john987
  • 65
  • 4
  • 6
    Accessing `test1->next` and other members after you freed the memory is *undefined behavior* - the program is free to do anything at that point – UnholySheep Sep 02 '22 at 16:59
  • 1
    ENABLE AND HEED YOUR COMPILER'S WARNINGS! 1) `void main()` is wrong. (It should be `int` not `void`.) 2) Pointers passed to `printf` must be cast to `void*`. 3) And of course, using a freed pointer is wrong. gcc's warnings catches all of these! – ikegami Sep 02 '22 at 17:00
  • A good practice is to null out the pointer after you free it so that errant accesses will fault rather than quietly grab the wrong data (or: grab temporarily ok-looking data) – Steve Friedl Sep 02 '22 at 17:08
  • @UnholySheep Including crashing, or "works" normally. – Zakk Sep 02 '22 at 17:14
  • 2
    Let's review: You asked `malloc` for some memory you could use. You stored some data into that memory. Then you called `free`, indicating that you were finished with that memory. But then you went and *used that memory some more*. Why did you do that? What did you expect would happen? If you were going to keep using it, you shouldn't have freed it. Once you freed it, you shouldn't keep using it. See also [this classic SO post about a hotel room](https://stackoverflow.com/questions/6441218). – Steve Summit Sep 02 '22 at 17:45
  • In general, when you ask `malloc` for some memory, then `free` it, some of the memory *does* get altered. Some but not all. So if you cheat, and peek, you might see that some of your data stayed the same, and that might give you the (completely false) impression that the memory doesn't get re-used for anything until you allocate it again later. But that's false. You *might* be able to see *some* of your old data in a block of freed memory, but it's not guaranteed, so you mustn't depend on it, in fact you really shouldn't inspect freed memory at all. – Steve Summit Sep 02 '22 at 17:56
  • The reason that some of the memory typically gets modified is that `malloc`/`free` have to maintain bookkeeping information about the available blocks of memory, and which are allocated and which are free. – Steve Summit Sep 02 '22 at 17:58
  • One more point: You can't depend on the data of your next node being 0, either, because you never initialize it. It could be anything. – Steve Summit Sep 02 '22 at 17:59

1 Answers1

1

Dereferencing a freed pointer is undefined behaviour. Once you do this, all rules are out the window. It could work as expected, it could produce nonsense results, or it could crash. And how it behaves can change between runs. As such, it's useless to try to make sense of the program's behaviour.

ikegami
  • 367,544
  • 15
  • 269
  • 518