ctypes:structure,size_t字段

时间:2018-03-27 00:38:22

标签: python ctypes

我正在尝试通过ctypeslibnfc实现python-to-c绑定。 我有一个结构,这是一个错误的变体:

class nfc_iso14443a_info(Structure):
    _fields_ = [
        ('abtAtqa',  c_uint8 * 2),
        ('btSak',    c_uint8),
        ('szUidLen', c_uint8 * 8),
        ('abtUid',   c_uint8 * 10),
        ...

在调试会话期间的某个时刻,它看起来像这样: enter image description here

这里的问题是我希望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),
        ...

enter image description here

我在这里缺少什么?

2 个答案:

答案 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_int64c_longc_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)