Структура Python Ctypes с битовыми полями имеет другое расположение памяти, чем структура в C

#python #c #ctypes #bit-fields

#python #c #ctypes #битовые поля

Вопрос:

У меня есть конкретные вопросы о 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*)(amp;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 amp;amp; ./a
size 4
ab ef 34 12
$ gcc sample.c -o a -m32 -std=gnu99 -w amp;amp; ./a
size 4
ab ef 34 12
  

Пока все в порядке, но когда я пишу эквивалентную структуру на python с использованием ctypes, я получаю другое определение:

 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
  

и если я изменю тип n3 в python с 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 поскольку похоже, что это ошибка, буду продолжать обновлять этот пост любыми обновлениями.

Комментарии:

1. Похоже, эта проблема связана с github.com/python/cpython/pull/19850 но без прагм. Я также попытаюсь продолжить там.

Ответ №1:

Хм.

Здесь (https://docs.python.org/2.5/lib/ctypes-bit-fields-in-structures-unions.html ) это говорит о том, что

Битовые поля возможны только для целых полей, битовая ширина указывается как третий элемент в кортежах полей:

Так что, возможно, вам нужно использовать type c_int для битовых полей?

Похоже, что sizeof здесь может передавать struct по значению, которое не поддерживается ctypes .

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

Комментарии:

1. Привет, спасибо за ответ. В документе указаны целочисленные поля, а в моем POV явно не указано c_int , поэтому любой другой тип по-прежнему в порядке. Теперь, если я использую c_int , это работает, и это будет так же, как в моем третьем случае (где я использовал c_int32 ), поскольку c_int32 может быть псевдонимом для c_int в зависимости от платформы. ` класс ctypes.c_int32 представляет 32-разрядный подписанный тип данных C int. Обычно псевдоним для c_int . `