ctypes:结构,size_t 字段
ctypes: structure, size_t field
我正在尝试通过 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
是等于 7 的 64 位无符号整数。更准确地说,它必须匹配 nfc-types.h
中的 size_t szUidLen;
。所以我尝试了一个明显的变体并将 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),
...
我在这里错过了什么?
这里的问题是您尝试映射的 C 结构已打包,如文档的 Structure/union alignment and byte order 部分中(简洁地)解释的那样:
By default, Structure and Union fields are aligned in the same way the C compiler does it. It is possible to override this behavior be specifying a _pack_
class attribute in the subclass definition. This must be set to a positive integer and specifies the maximum alignment for the fields. This is what #pragma pack(n)
also does in 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
,即 little-endian int64 for 7
,而第二个获取字节 0, 0, 0, a, b, c, d, e
,其中 abcde
是下一个字段的前 5 个字节,对于某个巨大的数字,它是 little-endian int64(除非下一个字段恰好为 0)。
当然你不想只是猜测这就是问题所在。如果你的 Structure
基于来自 C 头文件的 struct
,那么只有当头文件或编译标志指定一些 non-default 包装时,这才会是真的,比如使用的 #pragma pack(1)
通过 MSVC。如果你的 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 位整数而不是 8 位整数数组。
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)
我正在尝试通过 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
是等于 7 的 64 位无符号整数。更准确地说,它必须匹配 nfc-types.h
中的 size_t szUidLen;
。所以我尝试了一个明显的变体并将 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),
...
我在这里错过了什么?
这里的问题是您尝试映射的 C 结构已打包,如文档的 Structure/union alignment and byte order 部分中(简洁地)解释的那样:
By default, Structure and Union fields are aligned in the same way the C compiler does it. It is possible to override this behavior be specifying a
_pack_
class attribute in the subclass definition. This must be set to a positive integer and specifies the maximum alignment for the fields. This is what#pragma pack(n)
also does in 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
,即 little-endian int64 for 7
,而第二个获取字节 0, 0, 0, a, b, c, d, e
,其中 abcde
是下一个字段的前 5 个字节,对于某个巨大的数字,它是 little-endian int64(除非下一个字段恰好为 0)。
当然你不想只是猜测这就是问题所在。如果你的 Structure
基于来自 C 头文件的 struct
,那么只有当头文件或编译标志指定一些 non-default 包装时,这才会是真的,比如使用的 #pragma pack(1)
通过 MSVC。如果你的 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 位整数而不是 8 位整数数组。
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)