#pragma pack效果

Cen*_*noc 212 c c-preprocessor pragma-pack

我想知道是否有人可以向我解释#pragma pack预处理器语句的作用,更重要的是,为什么人们会想要使用它.

我查看了MSDN页面,它提供了一些见解,但我希望能从有经验的人那里听到更多.我以前在代码中看过它,虽然我似乎无法找到它的位置.

Nic*_*yer 390

#pragma pack指示编译器打包具有特定对齐的结构成员.大多数编译器在声明结构时会在成员之间插入填充,以确保它们与内存中的适当地址对齐(通常是类型大小的倍数).这避免了与访问未正确对齐的变量相关联的某些体系结构上的性能损失(或彻底错误).例如,给定4字节整数和以下结构:

struct Test
{
   char AA;
   int BB;
   char CC;
};
Run Code Online (Sandbox Code Playgroud)

编译器可以选择将结构放在内存中,如下所示:

|   1   |   2   |   3   |   4   |  

| AA(1) | pad.................. |
| BB(1) | BB(2) | BB(3) | BB(4) | 
| CC(1) | pad.................. |
Run Code Online (Sandbox Code Playgroud)

并且sizeof(Test)将是4×3 = 12,即使它只包含6个字节的数据.#pragma(据我所知)最常见的用例是在使用硬件设备时,您需要确保编译器不会在数据中插入填充,并且每个成员都遵循前一个成员.有了#pragma pack(1),上面的结构将如下布局:

|   1   |

| AA(1) |
| BB(1) |
| BB(2) |
| BB(3) |
| BB(4) |
| CC(1) |
Run Code Online (Sandbox Code Playgroud)

并且sizeof(Test)将是1×6 = 6.

有了#pragma pack(2),上面的结构将如下布局:

|   1   |   2   | 

| AA(1) | pad.. |
| BB(1) | BB(2) |
| BB(3) | BB(4) |
| CC(1) | pad.. |
Run Code Online (Sandbox Code Playgroud)

并且sizeof(Test)将是2×4 = 8.

  • 可能值得添加包装的缺点.(未对齐的对象访问在*best*情况下很慢,但在某些平台上会导致错误.) (70认同)
  • 似乎所提到的路线"性能损失"实际上可能是某些系统的好处http://danluu.com/3c-conflict/. (11认同)
  • 换句话说,double期望在8字节边界上.将其置于7字节边界将损害性能.但是把它放在一个16,32,64或4096字节的边界上,就不会超过8字节边界已经给你的东西了.您可以从CPU获得相同的性能,同时由于该帖子中列出的原因而获得更差的缓存利用率. (5认同)
  • @Pacerier不是真的.那篇文章讨论了一些相当极端的对齐(在4KB边界上对齐).CPU期望各种数据类型的某些最小对齐,但在最坏的情况下,这些对齐需要8字节对齐(不计算可能需要16或32字节对齐的向量类型).不对齐这些边界通常会给您带来明显的性能损失(因为负载可能必须以两个操作而不是一个操作完成),但是类型要么是对齐的,要么是不对齐的.更严格的对齐比没有购买任何东西(并废除缓存利用率 (4认同)
  • 因此,教训不是"打包是有益的"(打包违反了类型的自然对齐,从而伤害了性能),而只是"不要过度对齐超出要求" (4认同)

nmi*_*els 24

#pragma用于将非可移植(如在此编译器中)消息发送到编译器.禁用某些警告和打包结构的事情是常见的原因.如果在警告启用错误标志的情况下进行编译,则禁用特定警告特别有用.

#pragma pack特别用于表示正在打包的结构不应该使其成员对齐.当你有一个硬件的内存映射接口并且需要能够精确控制不同结构成员指向的位置时,它很有用.这显然不是一个好的速度优化,因为大多数机器在处理对齐数据方面要快得多.

  • 要在之后撤消,请执行以下操作:#pragma pack(push,1)和#pragma pack(pop) (16认同)
  • @malhal 这应该是答案的一部分。我来这里就是为了找这个 (2认同)

Jer*_*fin 15

它告诉编译器将结构中的对象对齐的边界.例如,如果我有类似的东西:

struct foo { 
    char a;
    int b;
};
Run Code Online (Sandbox Code Playgroud)

对于典型的32位机器,您通常"希望"在其间有3个字节的填充a,b因此b将落在4字节边界以最大化其访问速度(这是默认情况下通常会发生的情况).

但是,如果必须匹配外部定义的结构,则需要确保编译器完全根据外部定义布局结构.在这种情况下,你可以给编译器一个#pragma pack(1)告诉它不要在成员之间插入任何填充 - 如果结构的定义包括成员之间的填充,你明确地插入它(例如,通常与成员命名unusedNignoreN,或在其上的东西订购).

  • @Ashwin:将'b`放在4字节边界处意味着处理器可以通过发出一个4字节的负载来加载它.虽然它在某种程度上取决于处理器,但如果处于奇数边界,加载它将很有可能需要处理器发出两个单独的加载指令,然后使用移位器将这些部分组合在一起.典型的惩罚是该物品的负荷减少3倍. (8认同)
  • @SF .:可以-但即使不是这样也不要误导-在x86 CPU上(例如一个明显的例子),这些操作是在硬件上执行的,但是您仍然可以获得大致相同的一组操作和减速。 (2认同)

Pon*_*gge 7

数据元素(例如,类和结构的成员)通常在当前生成处理器的WORD或DWORD边界上对齐,以便改善访问时间.在不能被4整除的地址处检索DWORD需要在32位处理器上至少有一个额外的CPU周期.因此,如果您有三个char成员char a, b, c;,他们实际上倾向于占用6或12个字节的存储空间.

#pragma允许您覆盖它以实现更高效的空间使用,代价是访问速度,或者是为了保持不同编译器目标之间存储数据的一致性.从16位代码转换为32位代码,我感到非常有趣.我希望移植到64位代码会导致某些代码出现同样的问题.