简体   繁体   English

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

[英]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( xy )的整数成员可以正常打印,但是我不知道如何打印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 ,并将指针复制到对initialp的赋值中的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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM