0

Resizing memory in a separate function causes a memory leak.

Separated into two functions and leaky:

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

void memo(int* list);

int main (void)
{
    int* list = malloc(sizeof(int));
    memo(list);
    free(list);
}

void memo(int* list)
{
    int bigger = 5;
    int* temp;
    temp = realloc(list, sizeof(int)*bigger);
    if(temp == NULL)
    {
        exit(1);
    }
    list = temp;


}

Combined into one function and not leaky:

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


int main (void)
{
    int* list = malloc(sizeof(int));

    int bigger = 5;
    int* temp;
    temp = realloc(list, sizeof(int)*bigger);
    if(temp == NULL)
    {
        exit(1);
    }
    list = temp;
    free(list);
}

I'm writing a program that creates an int array with malloc(). Then, in another function, I expand the original array with realloc(), then free the array with free() in the original function. This gives me a memory leak according to valgrind. This doesn't happen however if I make them one function---which is fine for this simplified demo, but would make things very messy in my actual program. Is it possible that free() is freeing the amount of blocks the original malloc() called and not the new size of the memory block as specified in the other function?

For reference, here is the output for the leaky program in valgrind.


> ==323== Memcheck, a memory error detector
> ==323== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
> ==323== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
> ==323== Command: ./a.out
> ==323==
> ==323== Invalid free() / delete / delete\[\] / realloc()
> ==323==    at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==323==    by 0x40118A: main (smem.c:10)
> ==323==  Address 0x4a4a040 is 0 bytes inside a block of size 4 free'd
> ==323==    at 0x483DFAF: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==323==    by 0x4011C9: memo (smem.c:17)
> ==323==    by 0x40117E: main (smem.c:9)
> ==323==  Block was alloc'd at
> ==323==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==323==    by 0x401171: main (smem.c:8)
> ==323==
> ==323==
> ==323== HEAP SUMMARY:
> ==323==     in use at exit: 20 bytes in 1 blocks
> ==323==   total heap usage: 2 allocs, 2 frees, 24 bytes allocated
> ==323==
> ==323== 20 bytes in 1 blocks are definitely lost in loss record 1 of 1
> ==323==    at 0x483DFAF: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==323==    by 0x4011C9: memo (smem.c:17)
> ==323==    by 0x40117E: main (smem.c:9)
> ==323==
> ==323== LEAK SUMMARY:
> ==323==    definitely lost: 20 bytes in 1 blocks
> ==323==    indirectly lost: 0 bytes in 0 blocks
> ==323==      possibly lost: 0 bytes in 0 blocks
> ==323==    still reachable: 0 bytes in 0 blocks
> ==323==         suppressed: 0 bytes in 0 blocks
> ==323==
> ==323== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
> ==323==
> ==323== 1 errors in context 1 of 2:
> ==323== Invalid free() / delete / delete\[\] / realloc()
> ==323==    at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==323==    by 0x40118A: main (smem.c:10)
> ==323==  Address 0x4a4a040 is 0 bytes inside a block of size 4 free'd
> ==323==    at 0x483DFAF: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==323==    by 0x4011C9: memo (smem.c:17)
> ==323==    by 0x40117E: main (smem.c:9)
> ==323==  Block was alloc'd at
> ==323==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==323==    by 0x401171: main (smem.c:8)
> ==323==
> ==323== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
जलजनक
  • 3,072
  • 2
  • 24
  • 30
  • All function params are passed by value in C. This means `list` is a local variable in the function. Setting it does not set the caller's variable. See the duplicate for more details and ways to fix. – kaylum Apr 04 '22 at 23:10
  • `void memo(int* list)` -> `void memo(int** list)` – alex01011 Apr 04 '22 at 23:17
  • You change `memo`'s `list`, but not `main`'s. It's not just leaky, it's undefined behaviour because `free(list);` might attempt to free something that's already been freed. – ikegami Apr 04 '22 at 23:23
  • @kaylum so passing &list and changing memo's parameters to (int** list) (and then de-referencing the passed list) works, by why is that necessary? it seems superfluous as from my understanding it sends the address of the address of the original list, then immediately deferences the address of the address to just the address. why is it not possible to just pass the address? edit: i see the issue. the issue is in the list = temp line. And that's why the whole pass by reference vs value issue comes in. Alright, got it, thank you so much! – Johann Smith Apr 04 '22 at 23:24
  • Did you read the duplicate post? It is all explained in there. – kaylum Apr 04 '22 at 23:25
  • Re "*why is it not possible to just pass the address?*", But that's exactly what you want. You want to change the pointer (`list`), so you need to pass its address (`&list`). – ikegami Apr 04 '22 at 23:26
  • @kaylum yes it somehow slipped my mind that although realloc would be just fine without the address, assigning the local value of the address wouldn't change the value of the passed address. thank you for answering! – Johann Smith Apr 04 '22 at 23:28

0 Answers0