什么时候不应该使用#pragma pack?

在C中,当我们使用结构时,何时使用#pragma pack指令是不合适的?

问题的补充……

有人可以解释更多关于如何使用指针访问未对齐数据失败?

固件开发人员在这里。 #pragma pack是非常熟悉的领域。 我会解释一下。

通常,您不应该使用#pragma pack 。 是的,它会使你的结构在内存中变小,因为它消除了struct成员之间的所有填充。 但它可以使访问这些成员更加昂贵,因为成员可能不再符合他们的要求。 例如,在ARM体系结构中,4字节整数通常需要4字节对齐,但在压缩结构中它们可能不是。 这意味着编译器需要添加额外的指令来安全地访问该struct成员,或者开发人员必须逐个字节地访问它并手动重建int。 无论哪种方式,它都会产生比对齐访问更多的代码,因此您的结构最终会变小,但您的访问代码可能会变得越来越慢。

当结构必须与精确数据布局匹配时, 使用#pragma pack 。 当您编写匹配数据传输或访问规范的代码时,通常会发生这种情况…例如,网络协议,存储协议,访问HW寄存器的设备驱动程序。 在这些情况下,您可能需要#pragma pack来强制您的结构与规范定义的数据布局相匹配。 这可能会导致前一段中提到的性能损失相同,但可能是符合规范的唯一方法。

我会说你不应该打包,除非有一个非常好的理由这样做。

指定pack时,将删除所有填充。 因此,结构成员可能是未对齐的 – 这可能会产生性能影响。

在大多数体系结构中,底层访问必须与访问数据的对齐匹配。

这意味着,如果您具有32位值,则可以有效地访问它,如果它存储在可分为4的地址中。

如果使用#pragma pack ,变量的位置可以是任何内容,编译器必须逐个访问元素并将它们组合在一起。 具体来说,下面是生成的代码,用于读取V850E(嵌入式世界中流行的微控制器)上的普通int

 LD.W a[zero],r5 

相应地,以下是访问压缩结构中的int的代码:

 LD.BU g+3[zero],r1 SHL 8,r1 LD.BU g+2[zero],r6 OR r1,r6 SHL 8,r6 LD.BU g+1[zero],r7 OR r6,r7 SHL 8,r7 LD.BU g[zero],r1 OR r7,r1 

不使用压缩结构的另一个原因是,除非体系结构支持未对齐的指针访问,否则无法取消引用指向压缩结构体成员的指针。 这样做的原因是该点的类型将是一个普通的int指针,并且编译器不知道它必须访问它指向的任何东西。

我强烈建议你不要使用’#pragma pack’,除非绝对必要。 如果您可以控制结构定义,那么有一些技术可以确保结构布局是无填充的。 如果没有,更好的方法是将任何未对齐的数据复制到新的对齐的结构中,并在您的应用程序中使用它。

#pragma pack指令提供了满足此要求的方法。 该指令指定结构成员的包装对齐。 在看到pragma之后, pragma在第一个结构声明中生效。 Turbo C / C ++编译器不支持VC ++编译器这个function。

你永远不应该使用#pragma pack或类似的东西。 它总是会导致危险的可移植性问题。 例如,考虑结构:

 struct foo { char a; int b; } bar; 

并调用scanf("%d", &bar.b) 。 在没有错位访问的机器上,这将scanf内部scanf (或损坏内存!)! 这是因为&bar.b实际上并不是一个有效的int * – 它是未对齐的,但它传递给它的代码无法知道并解决它就像编译器那样,如果你刚刚编写了bar.b = 42;

打包结构的唯一用途是序列化,但这也会导致不可移植的文件。 您应该只编写正确的序列化函数。