【问题标题】:Why sizeof varies in given example of ctypes struct packing?为什么 sizeof 在给定的 ctypes 结构打包示例中会有所不同?
【发布时间】:2017-03-02 03:23:26
【问题描述】:

如果可以对以下代码的输出给出任何解释,我将不胜感激。我不明白为什么 sizeof(struct_2)sizeof(my_struct_2) 不同,提供 sizeof(struct_1)sizeof(c_int) 相同

似乎ctypes 以某种不同的方式将struct 包装在struct 中?

from ctypes import *

class struct_1(Structure):
    pass
int8_t = c_int8
int16_t = c_int16
uint8_t = c_uint8
struct_1._fields_ = [
    ('tt1', int16_t),
    ('tt2', uint8_t),
    ('tt3', uint8_t),
]

class struct_2(Structure):
    pass
int8_t = c_int8
int16_t = c_int16
uint8_t = c_uint8
struct_2._fields_ = [
    ('t1', int8_t),
    ('t2', uint8_t),
    ('t3', uint8_t),
    ('t4', uint8_t),
    ('t5', int16_t),
    ('t6', struct_1),
    ('t7', struct_1 * 6),
]

class my_struct_2(Structure):
    #_pack_ = 1  # This will give answer as 34
    #_pack_ = 4  #36
    _fields_ = [
    ('t1', c_int8),
    ('t2', c_uint8),
    ('t3', c_uint8),
    ('t4', c_uint8),
    ('t5', c_int16),
    ('t6', c_int),
    ('t7', c_int * 6),
]

print "size of c_int            : ", sizeof(c_int)
print "size of struct_1         : ", sizeof(struct_1)
print "size of my struct_2      : ", sizeof(my_struct_2)
print "siz of origional struct_2: ", sizeof(struct_2)

输出:

size of c_int            :  4
size of struct_1         :  4
size of my struct_2      :  36  
siz of origional struct_2:  34 ==> why not 36 ??

编辑: 重命名 t6->t7(struct_1 数组)并从 struct_2 中删除 pack=2。但我仍然看到struct_2my_struct_2 的大小不同

【问题讨论】:

  • 这是什么语言?如果是“C”,它使用了一些我不熟悉的扩展。
  • 在我看来像 Python,而不是 C。
  • 我认为是因为struct_2 被打包,所以t5 之后没有填充。但是my_struct_2 没有打包,所以在t5t6 之间有2 个字节的填充以使其字对齐。
  • 顺便说一句,为什么struct_2my_struct_2 中有两个t6 成员?第二个不应该是t7吗?
  • 添加了[python](删除[memory-alignment]以腾出空间),因为问题是关于Python“ctypes”模块的。

标签: python c struct padding ctypes


【解决方案1】:

差异源于结构布局中元素之间或之后是否存在填充,因为当大小不同时,较大的一个占的字节数比所有单个结构成员所需的字节数多。成员t5t6 就足以说明区别,如果省略t5(仅)则没有区别。

一个小实验表明,默认情况下(即当未指定 _pack_ 成员时),ctypes 为结构类型 struct_1 提供 2 字节对齐,但为类型 c_int 提供 4 字节对齐。或者它在我的系统上,无论如何。 ctypes 文档声称,默认情况下,它的结构布局方式与系统的 C 编译器(默认情况下)相同,而且似乎确实如此。考虑这个 C 程序:

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>

int main() {
    struct s {
        int16_t x;
        int8_t  y;
        uint8_t z;
    };
    struct t1 {
        int16_t  x;
        struct s y;
    };
    struct t2 {
        int16_t x;
        int     y;
    };

    printf("The size of int is       %zu\n", sizeof(int));
    printf("The size of struct s  is %zu\n", sizeof(struct s));
    printf("The size of struct t1 is %zu\n", sizeof(struct t1));
    printf("The size of struct t2 is %zu\n", sizeof(struct t2));
    printf("\nThe offset of t1.y is    %zu\n", offsetof(struct t1, y));
    printf("The offset of t2.y is    %zu\n", offsetof(struct t2, y));
}

它对我的输出(在 x86_64 上使用 GCC 4.8 的 CentOS 7 上)是:

int的大小为4
struct s 的大小为 4
struct t1 的大小为 6
struct t2 的大小为 8

t1.y 的偏移量为 2
t2.y 的偏移量为 4

观察intstruct s 的大小相同(4 个字节),但编译器将struct s 对齐在struct t1 内的2 字节边界上,而它对齐intstruct t2 内的 4 字节边界上。这与同一系统上 ctypes 的行为完美匹配。

至于 为什么 GCC 选择它的对齐方式,我观察到如果我将 int 类型的成员添加到 struct s 然后 GCC 切换到使用 4 字节对齐结构,以及将结构中int 的偏移量(默认情况下)安排为 4 个字节的倍数。可以合理地得出结论,GCC 正在布局结构内的成员并选择整个结构的对齐方式,以便每个对齐的结构实例的所有成员本身自然对齐。但是请注意,这只是一个示例。 C 实现在很大程度上取决于选择结构布局和对齐要求。

【讨论】:

  • ctypes 为结构类型 struct_1 提供 2 字节对齐 >> 如果我明确使用 __pack_=4 ,那么我会看到不同的大小。
  • 如果我们在 C 代码中使用对齐和打包,那么它会给出预期的大小 struct t1 { int16_t x;结构 y; } 属性 ((packed, aligned(4))); int 的大小是 4 struct s 的大小是 4 struct t1 的大小是 8 struct t2 的大小是 8 t1.y 的偏移量是 2 t2.y 的偏移量是 4 我们如何得到同样的行为在python中使用ctypes? pack = 4 不起作用。
  • @AmitSharma,你误解了_pack_ 的含义。它设置最大对齐。通过将其设置为较小的数字,您可以获得更少的填充,甚至没有,但您不能通过将其设置为更大的数字来强制更多的填充。但是,您可以通过添加一个原本会忽略的成员来手动填充结构。
  • @AmitSharma,据我所知,ctypes 没有用于指定 最小 对齐的内置机制(GCC 的 __attribute__((aligned)) 的效果)。
猜你喜欢
  • 1970-01-01
  • 2012-12-24
  • 1970-01-01
  • 2011-03-04
  • 2019-08-17
  • 1970-01-01
  • 1970-01-01
  • 2018-11-18
  • 2021-12-15
相关资源
最近更新 更多