我已经定义了一个简单的C结构,称为TestStruct
和一个函数init_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;
TestStruct initial = {x, y, msg};
p = malloc(sizeof(TestStruct));
*p = initial;
return p;
}
我使用.so
将C代码编译成gcc
文件。然后,在Python中,我想使用ctypes
创建一个绑定,该绑定可以访问C结构的所有成员
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)
myStruct = _init_struct(1, 4, ctypes.c_char_p(b'hello world'))
print(myStruct.contents.x, myStruct.contents.y, myStruct.contents.msg)
该结构的整数成员(x
和y
可以正常打印,但是我不知道如何打印msg
指向的字符串。最终没有看到预期的hello world
,而是看到了一个字节字符串b'\x01
。我从其他阅读中得出的直觉是,我将截断真实的更长的字符串,并且只显示第一个字节。
答案 0 :(得分:0)
您正在将ctypes.c_char_p(b'hello world')
传递到init_struct
,并将指针复制到c_char_p
和initial
的分配中的p
块中。但是,指向c_char_p
块的指针仅在调用init_struct
的期间有效,即,一旦init_struct
返回,则{{1} }指针将不再有效,对其进行访问将是未定义的行为。换句话说,您在c_char_p
中使用的指针的副本是悬空的,并且永远不能在myStruct.msg
之外访问。
请记住,init_struct
确实不违反了Python的垃圾回收(GC)规则。在这一行中,ctypes
myStruct = _init_struct(1, 4, ctypes.c_char_p(b'hello world'))
将分配一些ctypes
对象,将其复制到字符串b c_char_p
中,将其终止,并将其原始指针传递给该内存到C端。 。然后C端运行,您的代码获取该指针的副本。当C面返回时,hello world
释放对ctypes
对象的引用。然后,Python的GC发现c_char_p
不再被引用,因此被垃圾回收了。因此,您最终在c_char_p
中悬了一个指针。
正确的解决方案是在myStruct.msg
内克隆msg
内容并提供init_struct
函数以在完成克隆后释放该克隆内存像:
fini_struct
然后是python端:
#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);
}