代码之家  ›  专栏  ›  技术社区  ›  Grief

C类型:结构,大小\u t字段

  •  1
  • Grief  · 技术社区  · 6 年前

    我试图通过以下方式实现python到c的绑定 ctypes 对于 libnfc 。 我有一个结构,这里有一个错误的变体:

    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 为等于7的64位无符号整数。更准确地说,它必须匹配 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 回复  |  直到 6 年前
        1
  •  3
  •   abarnert    6 年前

    这里的问题是,您试图映射的C结构是打包的,正如 Structure/union alignment and byte order 文件部分:

    默认情况下,结构字段和联合字段的对齐方式与C编译器相同。可以通过指定 _pack_ 子类定义中的class属性。这必须设置为正整数,并指定字段的最大对齐方式。这是什么 #pragma pack(n) 在MSVC中也是如此。

    只有当您已经知道C中的打包和对齐时,这才有意义,但它并没有那么复杂。

    默认情况下,C结构元素将对齐,以从良好的边界开始。例如,8位int后面的32位int不是从字节1-4运行,而是从字节4-7运行(字节1-3是未使用的填充)。所以 ctypes 遵循相同的规则。

    这意味着 szUidLen 当它被定义为8位整数的数组时,从字节3-10运行;当它被定义为64位整数时,它与字节8-15(或4-11,取决于您的编译器)对齐。您可以通过打印 nfc_iso14443a_info.szUidLen.offset

    第一个得到字节 7, 0, 0, 0, 0, 0, 0, 0 ,即little endian int64 7 ,而第二个获取字节 0, 0, 0, a, b, c, d, e 哪里 abcde 是下一个字段的前5个字节,对于一些大的数字,它是小的endian int64(除非下一个字段恰好是0)。

    当然,你不想仅仅猜测这就是问题所在。如果您基于 Structure struct 对于C标头,只有当标头或编译标志指定一些非默认打包时,这才是真的,例如 #pragma pack(1) MSVC使用。如果您基于 结构 在类似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),
            ...
    

    现在 苏德伦 从字节3-10运行,但它被解释为64位int而不是8位int的数组。

        2
  •  -2
  •   jno    6 年前
    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\U类型 已对齐:

    In [7]: c.alignment(c.c_char * 8), c.alignment(c.c_size_t)
    Out[7]: (1, 8)