返回的字符串的ctypes视图c\u char\p字段

2024-04-19 22:39:54 发布

您现在位置:Python中文网/ 问答频道 /正文

我定义了一个名为TestStruct的简单C结构和一个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;
}

我使用gcc将C代码编译成.so文件。然后,在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。我从其他阅读中得到的预感是,我正在截断真正的、较长的字符串,只显示第一个字节。你知道吗


Tags: 字符串soincludeinitcontentsmsgctypes结构
1条回答
网友
1楼 · 发布于 2024-04-19 22:39:54

您正在将ctypes.c_char_p(b'hello world')传递给init_struct,并将赋值中指向c_char_p块的指针复制到initialp。但是,指向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对象,复制字符串bhello world,null终止它,并将指向该内存的原始指针传递给C端。然后运行C端,代码获取该指针的副本。当C端返回时,ctypes释放对c_char_p对象的引用。Python的GC发现c_char_p不再被引用,因此它被垃圾回收。因此,最终在myStruct.msg中得到一个悬空指针。你知道吗

正确的解决方案是在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);
}

然后是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)

相关问题 更多 >