如何将如下所示的C结构表示为python ctypes并对其进行访问?它具有结构数组作为元素,并带有另一个结构。
struct hello {
__s64 f1;
__u64 f2;
__u64 f3;
__s32 f4;
__u32 f5;
};
struct world {
__u64 f1;
__u64 f2;
__u16 f3;
__u16 f4;
__u32 f5;
struct hello h[0];
};
我尝试了以下操作,但没有用:
from ctypes import *
class hello(Structure):
_pack_ = 1
_fields_ = [ ("f1", c_longlong),
("f2", c_ulonglong),
("f3", c_ulonglong),
("f4", c_long),
("f5", c_ulong)
]
class world(Structure):
_pack_ = 1
_fields_ = [ ("f1", c_ulonglong),
("f2", c_ulonglong),
("f3", c_uint16),
("f4", c_uint16),
("f5", c_uint32),
("f6", hello) #Is this correct?
]
答案 0 :(得分:2)
您所做的不正确。为了使world
的最后一个成员是N个hello
的数组,它的类型必须为hello * N
,而不是hello
。
但是在这里,N为0,所以这实际上不起作用。
在C中,这称为“结构黑客”或“结构数组黑客”。 world
结构不是可以按原样构建并传递的真实结构。相反,您要做的是m分配sizeof(world) + N * sizeof(hello)
个字节,将其强制转换为world *
,然后将该指针传递给周围。由于数组索引不会在C语言中进行任何类型检查,因此my_world->h[2]
就可以正常工作(假设您在hello
的末尾分配了至少3个world
s的足够内存)。
但是它不能在Python中工作。
嗯,它可以多种,但是您必须做更多的工作。
第一种选择是不实际全局定义world
,而是在每次要处理Structure
时定义一个新的world
类。
例如。假设我们调用了一些函数来告诉我们有多少个问候:
n = lib.number_of_hellos()
现在我们要调用一个函数,该函数为我们提供了包含许多问候的世界。所以:
class world(Structure):
_pack_ = 1
_fields_ = [ ("f1", c_ulonglong),
("f2", c_ulonglong),
("f3", c_uint16),
("f4", c_uint16),
("f5", c_uint32),
("f6", hello * n)
]
这时,您可以更改restype
中的lib.get_world
以返回POINTER(world)
,也可以强制转换返回的结果。
或者,您可以将world
定义为始终具有1个hello
,但是,您可以构造一个{{1而不是直接从hello
指针访问world
}}指向hello * N
地址的指针(或仅指向hello
的地址加上类型的world
的偏移量;两种方法都相同)。
如果您需要创建一个包含N个hello
的世界才能传递给C怎么办?
您可以再次使用第一个技巧,构造一个新的hello
类,该类以N个world
的数组结尾,然后创建一个实例以将其传递给C。
或者您可以使用与C代码相同的技巧:保留全局单{hello
hello
类,并通过创建缓冲区然后投射该对象来构造world
对象到world
,或构造一个world
对象,然后在其上调用world
。
您还可以采取其他解决方法来简化生活。例如,如果您是自己编写C代码(并且不能只是不使用struct hack),则可以轻松添加一对由no-resize
hello
组成的C函数。 ,world
的数组以及N-hello
hello
中的计数,反之亦然。
或者,或者,仅使world
类型在末尾具有指向hellos数组的指针,而不是直接包含它,并编写C函数以在它们之间来回转换。
然后,您只需nonhackyworld
设置这些函数,并在需要传递或处理ctypes
时将它们调用(甚至将它们设置为其他函数的值类型转换器)。
即使您不能添加C函数,也可以在world
中编写这些函数。它们将使创建缓冲区和强制转换指针变得非常丑陋,但是您只需编写一次(并调试segfaults),然后在任何地方使用它们。