我在C中有一个打包结构,我想在Python中解析。我注意到C(使用GCC 4.9.2)和Python 3.4.2中的ctypes库之间的运算符sizeof
返回的结构大小与位域有所不同。
以下C代码按预期打印 5 :
#include <stdio.h>
#include <stdint.h>
typedef struct __attribute__((packed)) {
uint32_t ch0 : 20;
uint32_t ch1 : 20;
} pkt_t;
int main(){
printf("sizeof(pkt_t): %d\n", sizeof(pkt_t));
return 0;
}
虽然Python中的(相同)代码打印 8
import ctypes
class Packet(ctypes.LittleEndianStructure):
_pack_ = 1
_fields_ = [
('ch0', ctypes.c_uint32, 20),
('ch1', ctypes.c_uint32, 20),
]
print(ctypes.sizeof(Packet()))
看起来_pack_ = 1
相当于C中的__attribute__((aligned(1)))
,而不是__attribute__((packed, aligned(1)))
,它会使结构尽可能紧密地打包。有没有办法为ctypes结构启用packed
属性?
答案 0 :(得分:1)
ctypes文档https://docs.python.org/3/library/ctypes.html#structure-union-alignment-and-byte-order明确指出
默认情况下,“结构”和“联合”字段的对齐方式与 C编译器可以做到。可以通过以下方式覆盖此行为: 在子类定义中指定 pack 类属性。这个 必须设置为正整数并指定最大对齐 为领域。这是在MSVC中#pragma pack(n)的作用。
这意味着它们不是引用gcc的内容,而是引用MSVC #pragma pack
。参见:https://docs.microsoft.com/en-us/cpp/preprocessor/pack?view=vs-2019
n
(可选)指定用于打包的值(以字节为单位)。如果 未为模块设置编译器选项/ Zp,默认值 当n为8时。有效值为1、2、4、8和16。 成员位于n的倍数或倍数的边界上 成员大小中的较小者。
因此,观察到此行为的原因之一是MSVC与gcc的编译器详细信息。
gcc文档https://gcc.gnu.org/onlinedocs/gcc-4.9.4/gcc/Type-Attributes.html#Type-Attributes(很抱歉,gcc-4.9.2的文档在gcc页面上不可用),另一方面明确指出:
此属性(打包),附加到结构或联合类型定义中,用于指定 该结构的每个成员(零宽度位域除外) 或放置联合以最小化所需的内存。当连接到 一个枚举定义,它表示最小的整数类型 应该使用。
在这种情况下,它们对边界要求一无所知,而内存占用量则最小。如果有必要在字节边界上放置一个位字段,则可能是另一个讨论的主题。
对于我来说,为什么__attribute__((aligned(1)))
实际按预期设置结构似乎是合乎逻辑的并且与我一致,因为它明确地强制执行结构成员的字节对齐,而通过指定__attribute__((packed))
似乎不是这种情况。在第一种情况下,gcc编译器因此也对位字段强制执行字节对齐。
总结:
__attribute__ ((aligned (n)))
要求对齐结构(成员)__attribute__ ((__packed__))
请求,用于最大程度地减少内存占用,即使这意味着位域的字节边界已越过答案 1 :(得分:0)
我认为对齐不是这里的问题,因此在python中将对齐设置为1并没有达到预期的效果。
问题是C中的位域填充了给定的类型,但没有越过边界。 uint32_t具有32位,每个成员需要20位。因此,每个成员将放入一个单独的uint32_t中,总共8个字节。
打包属性以某种方式导致gcc忽略该规则并总共使用40bit。
另一方面,Python ctypes遵循该规则并获得8。