ctypes-查看返回的结构的c_char_p字段

时间:2018-12-14 21:14:43

标签: python c struct binding ctypes

我已经定义了一个简单的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)

该结构的整数成员(xy可以正常打印,但是我不知道如何打印msg指向的字符串。最终没有看到预期的hello world,而是看到了一个字节字符串b'\x01。我从其他阅读中得出的直觉是,我将截断真实的更长的字符串,并且只显示第一个字节。

1 个答案:

答案 0 :(得分:0)

您正在将ctypes.c_char_p(b'hello world')传递到init_struct,并将指针复制到c_char_pinitial的分配中的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);
}