Python ctypes: как освободить память? Ошибка неверного указателя

Я хочу получить строку из библиотеки C / C ++ с ctypes в Python. Мой код выглядит так:

Код в lib:

const char* get(struct something *x)
{
[...]
// buf is a stringstream
return strdup(buf.str().c_str());
}

void freeme(char *ptr)
{
free(ptr);
}

Код Python:

fillprototype(lib.get, c_char_p, POINTER(some_model)])
fillprototype(lib.freeme, None, [c_char_p])

// what i want to do here: get a string into python so that i can work
// with it and release the memory in the lib.
c_str = lib.get(some_model)
y = ''.join(c_str)
lib.freeme(c_str)

Если я печатаю () c_str, все есть. Проблема (или кажется) в последней строке Python. Я не могу освободить память — библиотека получает неверный указатель. Что я здесь не так делаю? (И, пожалуйста, не предлагайте boost :: python или около того).

*** glibc detected *** python2: munmap_chunk(): invalid pointer: 0x00000000026443fc ***

15

Решение

Как отметил Дэвид Шварц, если вы установите restype в c_char_p, ctypes возвращает обычный строковый объект Python. Простой способ обойти это — использовать void * и приведем результат:

string.c:

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

char *get(void)
{
char *buf = "Hello World";
char *new_buf = strdup(buf);
printf("allocated address: %p\n", new_buf);
return new_buf;
}

void freeme(char *ptr)
{
printf("freeing address: %p\n", ptr);
free(ptr);
}

Использование Python:

from ctypes import *

lib = cdll.LoadLibrary('./string.so')
lib.freeme.argtypes = c_void_p,
lib.freeme.restype = None
lib.get.argtypes = []
lib.get.restype = c_void_p

>>> ptr = lib.get()
allocated address: 0x9facad8
>>> hex(ptr)
'0x9facad8'
>>> cast(ptr, c_char_p).value
'Hello World'
>>> lib.freeme(ptr)
freeing address: 0x9facad8

Вы также можете использовать подкласс c_char_p, Оказывается, что ctypes не вызывает getfunc для подкласса простого типа.

class c_char_p_sub(c_char_p):
pass

lib.get.restype = c_char_p_sub

value Атрибут возвращает строку. Вы можете оставить параметр для freeme как более общий c_void_p, Это принимает любой тип указателя или целочисленный адрес.

20

Другие решения

дешевый способ освободить память — статически распределить ее в C и никогда не освобождать. работает, только если вы знаете максимальную сумму, которая вам нужна, конечно, и если вы знаете, что вы закончили до второго вызова вашей программы.

static char _externalStringBuffer[MAX_BUFFER_SIZE];
char *get() {
// put characters in _externalStringBuffer
return _externalStringBuffer;
}
1