【问题标题】:Python Ctypes struct with bitfields has a different memory layout than the struct in C具有位域的 Python Ctypes 结构与 C 中的结构具有不同的内存布局
【发布时间】:2020-10-03 19:45:49
【问题描述】:

我必须对 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', 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 sn-p 中我得到一个额外的字节?我可以理解它是否是 4 的倍数,但为什么是 5 个字节?
  3. 为什么 python 中结构定义的最后两个版本似乎与 C 的功能兼容?

更新

我打开了一个问题https://bugs.python.org/issue41932,因为看起来这是一个错误,将继续更新这篇文章。

【问题讨论】:

标签: python c ctypes bit-fields


【解决方案1】:

嗯。

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

位域只能用于整数域,位宽被指定为fields元组中的第三项:

所以也许您必须为位域使用类型c_int

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

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

【讨论】:

  • 您好,感谢您的回复。文档说整数字段,而我的 POV 并没有明确地说 c_int,所以任何其他类型仍然可以。现在,如果我使用 c_int 它可以工作,并且与我的第三种情况(我使用 c_int32)相同,因为 c_int32 可能是c_int 的别名,具体取决于平台。 `` class ctypes.c_int32 表示 C 32 位有符号 int 数据类型。通常是 c_int 的别名。 ``
猜你喜欢
  • 1970-01-01
  • 2011-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多