强制C++结构体紧密打包

54

我正在尝试读取一个二进制文件。问题在于文件的创建者没有将数据结构正确地对齐到其自然边界,而是将所有数据紧密打包。这使得使用 C++ 结构体读取数据变得困难。

有没有一种方法可以强制使 struct 紧密打包?

示例:

struct {
    short a;
    int b;
}

上述结构是8个字节: 2个用于short a,2个用于填充,4个用于int b。然而,在磁盘上,数据只有6个字节(没有用于对齐的2个字节的填充)。

请注意,实际数据结构有成千上万个字节和许多字段,包括一些数组,因此我希望不必逐个读取每个字段。


2
查询结构体的打包方法。请注意,某些体系结构要求对齐结构体以确保正确读取它们。 - A Person
http://en.cppreference.com/w/cpp/language/alignas 表示 alignas(0) 没有任何效果? - A Person
8
请编写代码将序列化的数据解码为内存中的表示形式。 - David Rodríguez - dribeas
2
如果你对这样的结构进行字节打包,请注意在访问该整数值时可能会出现段错误。 - zahir
2
在指定结构体中的位域时,尤其是如果有任何字段跨越字节或字边界时,请关注端序和字段顺序! - CarlH
显示剩余2条评论
2个回答

72

如果您使用的是GCC,您可以这样做:struct __attribute__ ((packed)) { short a; int b; }

在VC++上,您可以使用#pragma pack(1)。这个选项也被GCC支持

#pragma pack(push, 1)
struct { short a; int b; }
#pragma pack(pop)

其他编译器可能有选项来紧密地打包结构体而不进行填充。


谢谢。我目前正在使用GCC,这个可以工作。但是我已经留下了一个关于"pragma"的评论以备我们更改编译器时使用。 - steveo225
结构体继承会发生什么? - ar2015
1
如果我没记错,只有这个结构体会被紧密打包;继承的结构体必须再次使用它。如果这是从某个结构体继承而来的,那么它将被视为一种类型,并且其布局将保持在其声明中定义的方式。所有这些都是编译器特定的,因此您应该阅读编译器文档。 - legends2k
我相信它能工作; 实时示例。这很可能是一个环境问题。检查你的编译器版本、传递的标志等。尝试转储你的设置并查看是否有任何关闭值。 - legends2k
结构体__attribute__ ((packed)) 对我很有用。谢谢! - gkhanacer
显示剩余3条评论

17

你需要使用特定于编译器的非标准指令来指定1字节对齐方式。例如在Windows下:

#pragma pack (push, 1)

问题在于文件创建者没有花时间来正确地按字节对齐数据结构,一切都紧密打包在一起。

实际上,设计者做得很正确。填充是标准规定可以应用的内容,但规范并没有说明在什么情况下应该应用多少填充。标准甚至没有说明一个字节有多少个比特。尽管您可能认为即使这些内容没有被指定,它们在现代机器上应该仍然是相同的合理值,但事实并非如此。例如,在32位Windows机器上,填充可能是一种情况,而在64位版本的Windows中,它可能是另一种情况。也许会是相同的 - 这不是重点。重点是你不知道不同系统上的填充将是什么。

因此,“紧密打包”实际上是开发人员唯一能够做的事情 - 使用一些他可以合理确定每个系统都能够理解的打包方式。在这种情况下,通常理解的打包方式是在保存到磁盘或发送到网络时不使用填充结构。


6
我的那个声明的意思是,结构体元素的顺序本可以更好地排列,这样在许多不同情况下都能获得良好的对齐效果。并不是说他们只是简单地紧密压缩字节。 - steveo225

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接