为什么我们需要在C++中使用#pragma pack
来定义typedef
结构体?特别是在网络通信中使用这些结构体时。
为什么我们需要在C++中使用#pragma pack
来定义typedef
结构体?特别是在网络通信中使用这些结构体时。
#pragma pack
指令可以控制结构体成员的对齐方式。默认情况下,通常是8字节对齐,确保长度不超过8字节的成员按其大小的倍数对齐。例如,双精度或64位指针。如果读取或写入未对齐的双精度数据,可能会导致性能下降,如果它跨越了CPU缓存行边界,则通常会慢三倍左右。此对齐方式可能会在成员之间产生未使用的空间,称为填充。
这种对齐方式通常不适用于网络数据帧,它们倾向于紧密地打包而没有任何填充,因此需要使用 #pragma pack(push, 1) 指令。
#pragma pack指令修改了仅在指令后声明的结构体成员的当前对齐规则。它不直接影响结构体的对齐,但通过影响结构体成员的对齐方式,可能会根据对齐规则影响整个结构体的对齐。
如果您只需要让两个具有相同设置的系统进行通信(测试等),则可以发送结构体,前提是它们只是POD(普通旧数据)(指针、虚拟表、std::strings等不能使用)。因此,不需要打包。
如果这些系统没有相同的设置(或者未知),则需要发送序列化协议中的数据,因此打包结构将无法实现。最简单的方法是检查是否有有效的协议可用。
编译器会“填充”结构体的字段和子字段,即在内存中用空白的内存块组织它们,这样做是为了增加效率,正如Hans Passant的回答所解释的那样。
不同的编译器/目标/优化设置可能会以不同的方式进行填充,将相等的“逻辑”结构体转换为不相等的内存表示,因此,如果两台通信机器之间传递结构体,则它们可能无法理解彼此。
#pragma pack是限制编译器填充的方法。
#pragma pack(push, 1)命令编译器完全不进行填充。如果对两个在不同代码中定义的相同结构体执行此操作,则它们在内存中的表示将完全相同。然后,您可以使用指针和sizeof()将一个应用程序中结构体的内容通过某个协议安全地发送到另一个应用程序中,并在另一侧使用指针和sizeof()将其直接写入相同的结构体中。