Вы передаете ctypes.c_char_p(b'hello world')
в init_struct
и копируете указатель на блок c_char_p
в присваиваниях initial
и p
.Однако этот указатель на блок c_char_p
является действительным только на время вызова init_struct
, т. Е. После возврата init_struct
указатель c_char_p
больше не будет действительным и доступ к немуэто будет неопределенное поведение.Другими словами, копия этого указателя, которую вы взяли в myStruct.msg
, свисает и никогда не должна быть доступна за пределами init_struct
.
Помните, что ctypes
действительно НЕ нарушает сборку мусора в Python(GC) правила.В этой строке myStruct = _init_struct(1, 4, ctypes.c_char_p(b'hello world'))
ctypes
выделит некоторый объект c_char_p
, скопирует в строку b hello world
, завершит его нулевым значением и передаст необработанный указатель на эту память стороне C.Затем выполняется сторона C, и ваш код получает копию этого указателя.Когда сторона C возвращается, ctypes
освобождает свою ссылку на объект c_char_p
.Затем Python GC находит, что на c_char_p
больше нет ссылок, и поэтому он собирает мусор.Следовательно, вы получите висячий указатель в myStruct.msg
.
. Правильное решение - клонировать msg
содержимое внутри init_struct
и предоставить функцию fini_struct
для освобождения этогоклонировать память, когда вы закончите, что-то вроде:
#include <stdlib.h>
#include <stdio.h>
typedef struct {
int x;
int y;
char* msg;
} TestStruct;
TestStruct* init_struct(int x, int y, char* msg) {
TestStruct* p = malloc(sizeof(TestStruct));
p->x = x;
p->y = y;
p->msg = strdup(msg);
return p;
}
void fini_struct(TestStruct* p) {
free(p->msg);
free(p);
}
Тогда сторона Python:
import ctypes
import os
class PyStruct(ctypes.Structure):
_fields_ = [('x', ctypes.c_int),
('y', ctypes.c_int),
('msg', ctypes.c_char_p)]
lib = ctypes.cdll.LoadLibrary(os.path.abspath('/path/to/libstruct.so'))
_init_struct = lib.init_struct
_init_struct.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p]
_init_struct.restype = ctypes.POINTER(PyStruct)
_fini_struct = lib.fini_struct
_fini_struct.argtypes = [ctypes.POINTER(PyStruct)]
myStruct = _init_struct(1, 4, ctypes.c_char_p(b'hello world'))
print(myStruct.contents.x, myStruct.contents.y, myStruct.contents.msg)
# when you are done with myStruct
_fini_struct(myStruct)