abc xyz
abc xyz

Reputation: 129

Why doesn't valgrind detect memory leaks?

I'm trying to figure out why valgrind doesn't detect any errors when I deliberately avoid freeing memory. I have this small program. It reads numbers from the keyboard and prints the message You've entered <number>!. If the number was previously read then it prints You've already read <number>!.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct node {
    int number;
    struct node* next;
};

struct node* add(struct node* head, int number) {
    struct node* n;
    struct node* p;
    
    n = (struct node*)malloc(sizeof(struct node));
    n->number = number;
    n->next = NULL;

    if (head == NULL) {
        return n;
    }

    p = head;
    while (p->next != NULL) {
        p = p->next;
    }
    p->next = n;
    return head;
}

void clear(struct node* head) {
    if (head == NULL) {
        return;
    }
    clear(head->next);
    free(head);
}

int already_read(struct node* head, int number) {
    struct node* p;
    if (head == NULL) {
        return 0;
    }

    p = head;
    while (p!=NULL && p->number!=number) {
        p = p->next;
    }
    
    if (p == NULL) {
        return 0;
    }
    return 1;
}

int main(int argc, char** argv) {
    int number;
    struct node* head = NULL;
    while (scanf("%d", &number) == 1) {
        if (already_read(head, number)) {
            printf("You've already entered %d!\n", number);
        }
        else {
            head = add(head, number);
            printf("You entered %d!\n", number);
        }
    }
    clear(head);
    return 0;
}

I compiled it using gcc (This file is called program.c on my system, so I used the command gcc -Wall -g -o program program.c). It compiles fine. Then when I run it with valgrind (valgrind ./program), I enter some numbers to see that the program works and then I stop it with CTRL-C. I get no errors. Great.

LEAK SUMMARY:
    definetely lost: 0 bytes in 0 blocks
    indirectly lost: 0 bytes in 0 blocks
      possibly lost: 0 bytes in 0 blocks
    still reachable: 64 bytes in 4 blocks
         suppressed: 0 bytes in 0 blocks
ERROR SUMMARY: 0 errors from 0 context (suppressed: 8 from 6)

Then I go back in the code. I leave everything as it is, except I change the line in main() that reads clear(head) to //clear(head). So now the program doesn't free the space anymore. It has leaks. I recompile and run it again with valgrind. I enter the same input as before. And... I get the same error report. Exactly the same as the one above. I don't understand why this is happening. Shouldn't it report memory leaks, since I didn't free the memory? Am I doing something wrong?

Upvotes: 0

Views: 920

Answers (2)

Bodo Thiesen
Bodo Thiesen

Reputation: 2514

If you press CTRL+C you do not exit() your program, instead, you SIGINT your program. The difference is: The execution of your program ends abruptly, the clear() function will not be called and the function main() will also not return at all. valgrind will now see a program in a state, in which main() is still running, the life-time of local variable head has not ended yet and hence all memory referenced thereof is still reachable. As Paul Floyd already pointed out, when you quit your program by pressing q, the memory leak introduced by commenting out clear(head); will indeed show up. But for that you actually have to quit your program using q and not terminate it by CTRL+C.

Upvotes: 0

Paul Floyd
Paul Floyd

Reputation: 6946

If I run the code with the clear call commented out and enter 1, 2, 3, 2, q then I get

==60380== 48 (16 direct, 32 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2
==60380==    at 0x4839755: malloc (vg_replace_malloc.c:307)
==60380==    by 0x401178: add (test.c:14)
==60380==    by 0x401335: main (test.c:63)
==60380== 
==60380== LEAK SUMMARY:
==60380==    definitely lost: 16 bytes in 1 blocks
==60380==    indirectly lost: 32 bytes in 2 blocks
==60380==      possibly lost: 0 bytes in 0 blocks
==60380==    still reachable: 0 bytes in 0 blocks
==60380==         suppressed: 0 bytes in 0 blocks

Command valgrind --leak-check=full ./test

This is exactly what I would expect. This is on amd64 so each node is 16 bytes. 3 nodes are allocated (for 1, 2 and 3 but not for the duplicate 2 or the q). At the end of main() the head variable goes out of scope. So at termination no pointers exist that point to the head (1) node -> it is a definite leak. Due to the linked list, pointers do exist to the (2) and (3) nodes (the pointer from the head node to (2) and from (2) to (3). Thus the (2) and (3) nodes are indirect leaks.

Upvotes: 0

Related Questions