[英]ctypes - view c_char_p field of returned struct
I've defined a simple C struct called TestStruct
and a function init_struct
to create an instance and return a pointer to it 我定义了一个简单的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;
}
I compile the C code into a .so
file using gcc
. 我使用gcc
将C代码编译成.so
文件。 Then, in Python, I want to create a binding using ctypes
that can access all the members of the C struct 然后,在Python中,我想使用可以访问C结构的所有成员的ctypes
创建绑定。
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)
The integer members of the struct ( x
and y
) print out fine, but I can't figure out how to print the string that msg
points to. struct( x
和y
)的整数成员可以正常打印,但是我不知道如何打印msg
指向的字符串。 Instead of the expected hello world
, I end up seeing a bytes string b'\\x01
. 我没有看到预期的hello world
,而是看到了一个字节字符串b'\\x01
。 My hunch from other reading is that I'm truncating the true, longer string and only showing the first byte. 我从其他阅读中得出的直觉是,我将截断真实的更长的字符串,并且只显示第一个字节。
You are passing ctypes.c_char_p(b'hello world')
to init_struct
and copying the pointer to the c_char_p
block in the assignments to initial
and p
. 您将ctypes.c_char_p(b'hello world')
传递给init_struct
,并将指针复制到对initial
和p
的赋值中的c_char_p
块。 However, that pointer to the c_char_p
block is only valid for the duration of the call to init_struct
, ie, once init_struct
returns, that c_char_p
pointer will no longer be valid and accessing it will be undefined behavior. 但是,指向c_char_p
块的指针仅在对init_struct
的调用期间有效 ,即,一旦init_struct
返回,则该c_char_p
指针将不再有效,对其进行访问将是未定义的行为。 In other words, the copy of that pointer you took in myStruct.msg
is dangling and should never be accessed outside init_struct
. 换句话说,您在myStruct.msg
中使用的指针的副本是悬空的,并且永远不能在init_struct
之外访问。
Remember that ctypes
does NOT violate Python's Garbage Collection (GC) rules. 请记住, ctypes
不违反Python的垃圾回收(GC)规则。 In this line myStruct = _init_struct(1, 4, ctypes.c_char_p(b'hello world'))
ctypes
will allocate some c_char_p
object, copy in the string b hello world
, null terminate it, and pass the raw pointer to that memory to the C side. 在这一行myStruct = _init_struct(1, 4, ctypes.c_char_p(b'hello world'))
ctypes
将分配一些c_char_p
对象,在字符串b hello world
复制,将其终止为null,并将原始指针传递给该内存以C面。 Then the C side runs and your code takes a copy of that pointer. 然后C端运行,您的代码获取该指针的副本。 When the C side returns, ctypes
releases its reference to the c_char_p
object. 当C端返回时, ctypes
释放其对c_char_p
对象的引用。 Python's GC then finds that the c_char_p
is no longer referenced and so it gets garbage collected. 然后,Python的GC发现不再引用c_char_p
,因此将其垃圾回收。 Hence, you end up with a dangling pointer in myStruct.msg
. 因此,最终在myStruct.msg
有一个悬空指针。
The proper solution is to clone msg
contents inside init_struct
and provide a fini_struct
function to free that clone memory when you are done with it, something like: 正确的解决方案是克隆init_struct
msg
内容 ,并提供一个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);
}
Then the python side: 然后是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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.