Home > OS >  Mallocing and Freeing in C, but passing the pointer through Python via ctypes
Mallocing and Freeing in C, but passing the pointer through Python via ctypes

Time:11-15

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:

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.

  • Related