C++ using extern C library, unexpected memory leak

106 views Asked by At

I have created a simple C library is generated by one .c and one .h file.

// test.c
#include "test.h"
#include "stdlib.h"
void GetArray(int * array)
{
  array = (int *) calloc(2, sizeof(int));
  array[0] = 0;
  array[1] = 1;
}
// test.h
void GetArray(int * array);

I then generated a C++ executable that would link to the C library and call this function.

// Test.cpp
#include "Test.hpp"

int main(int argc, char *argv[])
{
  int * array;
  GetArray(array);
  delete[] array;
  return 0;
}

with header

// Test.hpp
extern "C" {
#include "test.h"
}

For completeness I include the Makefile

CC  = gcc
CPP = g++

# ----- C-based library -------
testlib.a: test.o
        ar rcs testlib.a test.o

test.o: test.c
        $(CC) -c $<

# ------ C++ executable that links to C-based library -----
Test: Test.o testlib.a
        $(CPP) Test.o -o $@ testlib.a

Test.o: Test.cpp
        $(CPP) -c $<

.PHONY: clean
clean:
        rm -f *.o Test testlib.a

I was surprised to see that despite the delete[] array; call in Test.cpp, that a valgrind LEAK SUMMARY report showed that 8 bytes of memory are definitely lost.

I alternatively tried to create array directly in Test.cpp without the call to the C-library function GetArray. That is

// Test.cpp
#include "Test.hpp"

int main(int argc, char *argv[])
{
  int * array;
  array = new int[2];
  array[0] = 0;
  array[1] = 1;
  delete[] array;
  return 0;
}

and observed that the valgrind report then showed no memory leaks. I am not sure why I can't clean memory from the C++ executable that was allocated in a function defined in the C-library.

2

There are 2 answers

1
selbie On

Change this:

void GetArray(int * array)
{
  array = (int *) calloc(2, sizeof(int));
  array[0] = 0;
  array[1] = 1;
}

To this:

void GetArray(int** array)
{
  *array = (int *) calloc(2, sizeof(int));
  (*array)[0] = 0;
  (*array)[1] = 1;
}

Then invoke as follows:

int* array;
GetArray(&array);

Otherwise, if you only declare it as GetArray(int*), then the parameter value array will get overwritten, but the caller's value for array is never changed. Remember, even pointers themselves are passed by value.

A more natural way of course is this:

int* GetArray()
{
  int* array = (int *) calloc(2, sizeof(int));
  array[0] = 0;
  array[1] = 1;
  return array;
}

Then invoked as:

int* array = GetArray();
2
Vlad from Moscow On

To change within the function GetArray the pointer array declared in main you need to pass it to the function by reference.

In C passing by reference means passing an object indirectly through a pointer to it. Thus dereferencing the passed pointer to an object you will get a direct accesss to the object pointed to by the pointer. Otherwise the function will create its own local variable (parameter) initialized by the value of the passed argument and will change the local variable. The variable used as the argument will stay unchanged.

You can imagine your function and its call the following way

GetArray(array);

//...

void GetArray( /*int * patm_array*/)
{
  int *parm_array = array;

  parm_array = (int *) calloc(2, sizeof(int));
  parm_array[0] = 0;
  parm_array[1] = 1;
}

As it is seen it is the local variable parm_array (I renamed the function parameter to distinguish it from the passed variable declared in main) that was changed within the function.

So you need to declare and define the function in C like

void GetArray( int **array )
{
  *array = (int *) calloc(2, sizeof(int));
  ( *array )[0] = 0;
  ( *array )[1] = 1;
}

and call it like

GetArray( &array );

In C++ you could declare and define the function like

void GetArray(int * &array)
{
  array = (int *) calloc(2, sizeof(int));
  array[0] = 0;
  array[1] = 1;
}

and call it like

GetArray( array );