我正在尝试通过ctypes
为libnfc
实现python-to-c绑定。
我有一个结构,这是一个错误的变体:
class nfc_iso14443a_info(Structure):
_fields_ = [
('abtAtqa', c_uint8 * 2),
('btSak', c_uint8),
('szUidLen', c_uint8 * 8),
('abtUid', c_uint8 * 10),
...
这里的问题是我希望szUidLen
是64位无符号整数,等于7.更确切地说,它必须与size_t szUidLen;
中的nfc-types.h
匹配。所以我尝试了一个明显的变体,并将c_uint8 * 8
更改为c_size_t
,但它不起作用:
class nfc_iso14443a_info(Structure):
_fields_ = [
('abtAtqa', c_uint8 * 2),
('btSak', c_uint8),
('szUidLen', c_size_t),
('abtUid', c_uint8 * 10),
...
我在这里缺少什么?
答案 0 :(得分:3)
这里的问题是,您正在尝试映射的C结构已经打包,正如(文档)的Structure/union alignment and byte order部分(简洁地)解释的那样:
默认情况下,Structure和Union字段的对齐方式与C编译器的对齐方式相同。可以通过在子类定义中指定
_pack_
类属性来覆盖此行为。必须将其设置为正整数,并指定字段的最大对齐方式。这就是#pragma pack(n)
在MSVC中的作用。
只有你已经知道C中的打包和对齐才有意义,但它并不复杂。
默认情况下,C结构元素对齐以在良好的边界上开始。例如,8位int后面的32位int不从字节1-4运行,它从字节4-7运行(字节1-3是未使用的填充)。因此,ctypes
遵循相同的规则。
这意味着,虽然szUidLen
从字节3-10开始运行,当它被定义为8位整数的数组时,它会对齐到字节8-15(或4-11,取决于当你的编译器被定义为64位int时。您可以通过打印nfc_iso14443a_info.szUidLen.offset
来查看此内容。
因此,第一个获取字节7, 0, 0, 0, 0, 0, 0, 0
,对于7
,它是little-endian int64,而第二个获取字节0, 0, 0, a, b, c, d, e
,其中abcde
是下一个字段的前5个字节,对于一些巨大的数字是小端int64(除非下一个字段恰好是0)。
当然,你不想猜这是问题所在。如果基于Structure
从{C}标头开始struct
,则只有在标头或编译标志指定某些非默认包装时才会出现这种情况,例如MSVC使用的#pragma pack(1)
。如果您将Structure
基于RFC数据包描述,那么对齐甚至不符合C规则,而是在您正在阅读的文档中的某处定义(尽管协议RFC几乎总是使用1字节对准)。
无论如何,文档不能很好地解释问题,但他们解释了解决方案:
class nfc_iso14443a_info(Structure):
_pack_ = 1
_fields_ = [
('abtAtqa', c_uint8 * 2),
('btSak', c_uint8),
('szUidLen', c_size_t),
('abtUid', c_uint8 * 10),
...
现在szUidLen
从字节3-10开始运行,但它被解释为64位int而不是8位整数数组。
答案 1 :(得分:-2)
from ctypes import *
c_size_t = c_unit64
继续
在定义._pack_=1
之前,您可能还需要指定_fields_
(如果编译器以这种方式生成代码)。
更新:可以在c_size_t
中键入c_ssize_t
(和ctypes
)。
注意:由于可能的对齐问题,(c_char * 8)
不等于c_int64
或c_long
(c_char
字段未对齐)。 ctypes.alignment(c_type)
可能会提示您 c_type 的对齐方式:
In [7]: c.alignment(c.c_char * 8), c.alignment(c.c_size_t)
Out[7]: (1, 8)