I would like to put a malloc
a function in C. I would then like to call this function from Python 3.10 via ctypes.DLL
. I then would like to free
it.
However, I get a segmentation fault. Here's my very simple C code:
#include <stdlib.h>
struct QueueItem {
void *value;
struct QueueItem *next;
};
struct Queue {
struct QueueItem* head;
struct QueueItem* tail;
};
struct Queue * new_queue(void * value) {
struct Queue* queue = malloc(sizeof(struct Queue));
struct Queue queue_ = { value, NULL };
return queue;
}
void delete_queue(struct Queue* queue) {
free(queue);
};
I'll compile this with gcc -fPIC -shared src/queue.c -o queue.so
, and thenn on the python side:
import ctypes
queue = ctypes.CDLL("./queue.so")
q = ctypes.POINTER(queue.new_queue(1))
print(q)
print(type(q))
queue.delete_queue(q)
But running this will yield:
-1529189344
<class 'int'>
Segmentation fault
The question is, how do I malloc in C, pass the pointer through python, and then free it again in C?.
Primary Resources Consulted:
- Passing pointer to DLL via Ctypes in Python
- Python Ctypes passing pointer to structure containing void pointer array
- https://docs.python.org/3/library/ctypes.html
- Messing around with ctypes.POINTER but it complains about types, and I'm not an expert in this area.
CodePudding user response:
If you don't define the restype
and argtypes
for a function, the restype
is assumed to be a C int
(c_int
), and the argument types are guessed at based on what you pass. The problem here is that the implicit restype
of C int
is (on a 64 bit system) half the width of a pointer, so the value returned by new_queue
is only half of a pointer (which is completely useless).
For safety, and error-checking, you should define both before calling a function, especially the restype
which can't be inferred.
So for your code, you might do:
import ctypes
queue = ctypes.CDLL("./queue.so")
queue.new_queue.argtypes = (c_void_p,)
queue.new_queue.restype = c_void_p
queue.delete_queue.argtypes = (c_void_p,)
queue.delete_queue.restype = None
q = queue.new_queue(1)
print(q)
print(type(q))
queue.delete_queue(q)
Note that passing along 1
as the argument to new_queue
is almost certainly incorrect, but I suspect it will work here since none of the code will actually try to use it.