具有位字段的Python Ctypes结构与C中的结构具有不同的内存布局



我必须具体询问Ctypes以及它们定义结构的方式。为了给您一点上下文,请考虑C中的以下示例,该示例定义了一个具有位字段的结构:

#include <stdint.h>
#include <stdio.h>
struct Version
{
uint8_t  n1;
uint8_t  n2;
uint32_t n3:16;
} vv;
int main(void)
{
vv.n1 = 0xab;
vv.n2 = 0xef;
vv.n3 = 0x1234;
uint8_t* ptr = (uint8_t*)(&vv);
printf("size %un", sizeof(vv));
for (int i = 0; i < sizeof(vv); ++i) printf("%2x ", ptr[i]);
printf("n");

return 0;
}

它似乎为32和64体系结构生成了相同的定义:

$ gcc sample.c -o a -m64 -std=gnu99 -w && ./a
size 4
ab ef 34 12
$ gcc sample.c -o a -m32 -std=gnu99 -w && ./a
size 4
ab ef 34 12

在那之前一切都很好,但当我使用ctypes在python中编写等效结构时,我得到了一个不同的定义:

from ctypes import *

class Version(Structure):
_fields_ = [
('n1', c_uint8, 8),
('n2', c_uint8, 8),
('n3', c_uint32, 16),
]

vv = Version()
vv.n1 = 0xab
vv.n2 = 0xef
vv.n3 = 0x1234
print('bytes', bytes(vv).hex())
print('size', sizeof(vv))

因为ctypes结构使用5个字节,而不是4个(C选择的字节(

$ python sample.py
bytes abef341200
size 5

如果我将python中n3的类型从c_uint32更改为c_uint16,它似乎与用C:编写的代码具有相同的布局

class Version(Structure):
_fields_ = [
('n1', c_uint8, 8),
('n2', c_uint8, 8),
('n3', c_uint16, 16),
]
...
$ python sample.py
bytes abef3412
size 4

如果我把所有东西都改成c_uint32:,我会得到同样的结果

class Version(Structure):
_fields_ = [
('n1', c_uint32, 8),
('n2', c_uint32, 8),
('n3', c_uint32, 16),
]
...
$ python sample.py
bytes abef3412
size 4

问题

  1. 如果Ctypes本机使用c库,为什么我会得到不同的结果
  2. 为什么在第一个python片段中我得到了一个额外的字节?我可以理解它是否是4的倍数,但为什么是5字节
  3. 为什么python中结构定义的最后两个版本似乎与C兼容

更新

我打开了一期https://bugs.python.org/issue41932由于看起来这是一个bug,我们将继续用任何更新来更新这篇文章。

嗯。

在这里(https://docs.python.org/2.5/lib/ctypes-bit-fields-in-structures-unions.html)上面说

位字段仅适用于整数字段,位宽被指定为字段元组中的第三项:

所以您可能必须对位字段使用类型c_int

这里的sizeof可能是按值传递结构的,而ctypes不支持这一点。

https://github.com/beeware/rubicon-objc/pull/157

最新更新