Search code examples
cmallocvalgrind

Phantom leaks with valgrind in C


Im writing a simple array. And how i think, do it correct in terms of leaks. But valgrind doesnt think so. Why? That phantom leak just because i do more malloc then free?

Code:

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

#define CAPACITY_INCREACE(arr) ((int)(arr->capacity * 1.5f))
#define CAPACITY_DEFAULT 5

typedef struct array
{
    int capacity;
    int size;
    int* arr;
} array;

array* create_array()
{
    array* arr = (array*)malloc(sizeof(array));
    arr->capacity = CAPACITY_DEFAULT;
    arr->size = 0;
    arr->arr = (int*)malloc(arr->capacity * sizeof(int));
    return arr;
}

void print_array(array *arr)
{
    printf("array, size - %d, capacity - %d\n", arr->size, arr->capacity);
    for(int i = 0; i < arr->size; ++i)
        printf("%d ", arr->arr[i]);
    printf("\n");
}

void realloc_array(array* arr, int size)
{
    int* new_arr = (int*)malloc(size * sizeof(int));
    memcpy((void*)new_arr, arr->arr, arr->size * sizeof(int));
    arr->arr = new_arr;
}

void add(array* arr, int element)
{
    if((arr->size + 1) <= arr->capacity)
    {
        arr->arr[arr->size++] = element;
        return;
    }
    
    arr->capacity = CAPACITY_INCREACE(arr);
    realloc_array(arr, arr->capacity);
    arr->arr[arr->size++] = element;
}

void delete_array(array *arr)
{
    free(arr->arr);
    free(arr);
}

int main(int argc, int** argv)
{
    array* arr = create_array();
    for(int i = 0; i < 8; i++)
        add(arr, i);
    print_array(arr);

    delete_array(arr);
    return 0;
}

Valgrind run and program compile

gcc -g array.c && ./a.out
valgrind --leak-check=full -s ./a.out

Info from valgrind, he show me where, but unfortunately im not understand what im doing wrong:

HEAP SUMMARY:
    in use at exit: 48 bytes in 2 blocks
    total heap usage: 5 allocs, 3 frees, 1,128 bytes allocated

 20 bytes in 1 blocks are definitely lost in loss record 1 of 2
    by 0x10920B: create_array (array.c:21)
    by 0x109443: main (array.c:71)
 
 28 bytes in 1 blocks are definitely lost in loss record 2 of 2
    by 0x10930C: realloc_array (array.c:43)
    by 0x1093CD: add (array.c:57)
    by 0x10946D: main (array.c:74)
 
 LEAK SUMMARY:
    definitely lost: 48 bytes in 2 blocks
    indirectly lost: 0 bytes in 0 blocks
      possibly lost: 0 bytes in 0 blocks
    still reachable: 0 bytes in 0 blocks
         suppressed: 0 bytes in 0 blocks

Solution

  • The function

    void realloc_array(array* arr, int size)
    {
        int* new_arr = (int*)malloc(size * sizeof(int));
        memcpy((void*)new_arr, arr->arr, arr->size * sizeof(int));
        arr->arr = new_arr;
    }
    

    is throwing away the original buffer arr->arr without freeing.

    Add call to free() to avoid memory leak.

    void realloc_array(array* arr, int size)
    {
        int* new_arr = (int*)malloc(size * sizeof(int));
        memcpy((void*)new_arr, arr->arr, arr->size * sizeof(int));
        free(arr->arr); /* add this */
        arr->arr = new_arr;
    }
    

    Or use standard realloc().

    void realloc_array(array* arr, int size)
    {
        int* new_arr = realloc(arr->arr, size * sizeof(int));
        if (new_arr == NULL) exit(1);
        arr->arr = new_arr;
    }
    

    Also note that casting results of malloc() family is considered as a bad practice.