具有位域的Python Ctypes结构与C中的结构具有不同的内存布局 问题更新

问题描述

我必须对 Ctypes 及其定义结构的方式提出具体问题。为了给您一些背景信息,请考虑以下C语言示例,该示例使用 bitfields 定义了一个结构:

#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 %u\n",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',('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_uint16,]
...

$ python sample.py
bytes abef3412
size 4

如果将所有内容都更改为c_uint32,我将得到相同的结果:

class Version(Structure):
    _fields_ = [
        ('n1',]
...

$ python sample.py
bytes abef3412
size 4

问题

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

更新

我打开了一个问题https://bugs.python.org/issue41932,因为它似乎是一个错误,将继续通过任何更新来更新此帖子。

解决方法

嗯。

https://docs.python.org/2.5/lib/ctypes-bit-fields-in-structures-unions.html)说

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

那么也许您必须对位域使用类型c_int

看来这里的sizeof可能是按值传递struct,而ctypes不支持。

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