0

I have this C code:

test.c

#define max_size 9
#define max_value 11

char ** my_function(char my_string[]){
    char **my_array = malloc(sizeof(char *) * max_size);
    if (!my_array){
        return NULL;
    }

    for( i = 0; i < max_size; i++){
         my_array[i] = malloc(sizeof(char)*max_value+1);
         if (!my_array[i]) {
             free (my_array);
             return NULL;
        }
    }

    for( i = 0; i < max_size; i++){
        // some code to set my_array[i]
    }
    return my_array;
}

Which I'm compiling with:

gcc -shared -Wl,-soname,libTest.so.1 -o _libTest.dll -fPIC test.c

And them I'm importing in Python like this:

test.py

from ctypes import *
from ctypes.util import find_library

libc = CDLL(find_library("c"))
my_lib = CDLL("_libTest.dll")

my_function = my_lib.my_function
my_function.argtypes = [c_char_p]
my_function.restype = POINTER(c_char_p)

my_string = "Hello World"
c_string = c_char_p(my_string)

res = my_function(c_string)
for i in xrange(9):
    result = res[i]
    # some code using with result
    libc.free(result)

libc.free(res)

But this raises this error:

Traceback (most recent call last):
  File "C:/Documents/Python/test.py", line 19, in <module>
    libc.free(res)
WindowsError: exception: access violation reading 0x005206CA

As this answer states:

The error you get indicates that your program is trying to write to memory address XXXXX, but isn't supposed to be writing to that memory address.

The above answer them uses some other tecnhics which don't involve mallocing any memory, thus is useful in my case, and also because I'm not trying to write in the memory, I'm trying to free it.

The error doesn't raise in the above line libc.free(result), so the memory allocated inside the my_array is set free, but not the my_array.

If I were to free it in C:

for ( i = 0; i < max_size; i ++){
    free(res[i]);
}
free(res); 

Which would be the right way to do, no error would raise. But this seem to not work in Python.

So the title question: How do I free nested mallocs from a C shared library in Python (ctypes)?

Community
  • 1
  • 1
f.rodrigues
  • 3,499
  • 6
  • 26
  • 62
  • 1
    You forgot to set `libc.free.argtypes = [c_void_p]`. But on Windows you shouldn't use `free` from Python's libc. Export `free` from the DLL to use the same runtime for both allocation and deallocation. – Eryk Sun Dec 31 '14 at 02:23
  • Also the simple type `c_char_p` has a `getfunc` that returns a Python string (or `bytes` in 3.x), so instead use `my_function.restype = POINTER(POINTER(c_char))` to ensure that `result` is a ctypes object. – Eryk Sun Dec 31 '14 at 02:37
  • How do I export the free from the dll? – f.rodrigues Jan 01 '15 at 20:05
  • I don't know how with MinGW, unless gcc supports def files. Barring that I'd just add `void free_result(void *)` that calls `free`. – Eryk Sun Jan 01 '15 at 20:08
  • BTW, the linker option `-soname,libTest.so.1` is meaningless for a DLL. `DT_SONAME` is present in ELF binaries (e.g. Linux), not Windows PE/COFF. – Eryk Sun Jan 01 '15 at 20:23

0 Answers0