Problem Code (chain.c):
#include <stdlib.h>
typedef struct node
{
int id;
struct node *next;
} node;
// =========================================================
// THE 5-MINUTE CHALLENGE:
// Fix this loop so Valgrind reports 0 errors and 0 leaks.
// =========================================================
void free_list(node *head)
{
node *cursor = head;
while (cursor != NULL)
{
free(cursor);
cursor = cursor->next; // <-- BUG: Reading memory we just freed!
}
}
int main(void)
{
// Build a simple 3-node list: [1] -> [2] -> [3] -> NULL
node *n1 = malloc(sizeof(node));
node *n2 = malloc(sizeof(node));
node *n3 = malloc(sizeof(node));
n1->id = 1; n1->next = n2;
n2->id = 2; n2->next = n3;
n3->id = 3; n3->next = NULL;
free_list(n1);
return 0;
}```
Valgrind Error:
==72347== Memcheck, a memory error detector ==72347== Copyright (C) 2002-2024, and GNU GPL’d, by Julian Seward et al. ==72347== Using Valgrind-3.26.0 and LibVEX; rerun with -h for copyright info ==72347== Command: ./chain ==72347== ==72347== Invalid read of size 8 ==72347== at 0x4001193: free_list (chain.c:20) ==72347== by 0x4001229: main (chain.c:35) ==72347== Address 0x4a9f048 is 8 bytes inside a block of size 16 free’d ==72347== at 0x48538BF: free (vg_replace_malloc.c:990) ==72347== by 0x400118E: free_list (chain.c:19) ==72347== by 0x4001229: main (chain.c:35) ==72347== Block was alloc’d at ==72347== at 0x4850858: malloc (vg_replace_malloc.c:447) ==72347== by 0x40011BB: main (chain.c:27) ==72347== ==72347== ==72347== HEAP SUMMARY: ==72347== in use at exit: 0 bytes in 0 blocks ==72347== total heap usage: 3 allocs, 3 frees, 48 bytes allocated ==72347== ==72347== All heap blocks were freed – no leaks are possible ==72347== ==72347== For lists of detected and suppressed errors, rerun with: -s ==72347== ERROR SUMMARY: 3 errors from 1 contexts (suppressed: 0 from 0)
Awesome challenge, credits to Gemini 4 Argon :)
My brain's POV ;)
~ I saw that the memory which is being accessed was being freed before using it, quite obvious!
~ Why not use a temporary copy of the variable that we want? Awesome, I made a tmp variable.
~ Then I quite bravely freed the cursor variable.
~ And finally to make the linked list point to the new element, I used `cursor = tmp.next;`.
EASY PEASY!
Ummm so yeah the code and the output of valgrind now look like:
chain.c:
#include <stdio.h> #include <stdlib.h>
typedef struct node { int id; struct node *next; } node;
void free_list(node *head) { node *cursor = head; while (cursor != NULL) { node tmp = *cursor; free(cursor); cursor = tmp.next; // <– BUG: Reading memory we just freed! } }
int main(void) { // Build a simple 3-node list: [1] -> [2] -> [3] -> NULL node *n1 = malloc(sizeof(node)); node *n2 = malloc(sizeof(node)); node *n3 = malloc(sizeof(node));
n1->id = 1; n1->next = n2;
n2->id = 2; n2->next = n3;
n3->id = 3; n3->next = NULL;
free_list(n1);
return 0;
}
Valgrind Output:
==72513== Memcheck, a memory error detector ==72513== Copyright (C) 2002-2024, and GNU GPL’d, by Julian Seward et al. ==72513== Using Valgrind-3.26.0 and LibVEX; rerun with -h for copyright info ==72513== Command: ./chain ==72513== ==72513== ==72513== HEAP SUMMARY: ==72513== in use at exit: 0 bytes in 0 blocks ==72513== total heap usage: 3 allocs, 3 frees, 48 bytes allocated ==72513== ==72513== All heap blocks were freed – no leaks are possible ==72513== ==72513== For lists of detected and suppressed errors, rerun with: -s ==72513== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
If you are reading this, buy a coffee for me... idk how...