我想知道是否有人可以向我解释一下#pragma包预处理器语句是做什么的,更重要的是,为什么人们想要使用它。

我查看了MSDN页面,它提供了一些见解,但我希望从有经验的人那里听到更多。我以前在代码中见过它,尽管我似乎再也找不到在哪里了。


当前回答

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

struct Test
{
   char AA;
   int BB;
   char CC;
};

编译器可以选择像这样在内存中布局结构:

|   1   |   2   |   3   |   4   |  

| AA(1) | pad.................. |
| BB(1) | BB(2) | BB(3) | BB(4) | 
| CC(1) | pad.................. |

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

|   1   |

| AA(1) |
| BB(1) |
| BB(2) |
| BB(3) |
| BB(4) |
| CC(1) |

sizeof(Test)是1 × 6 = 6。

使用#pragma pack(2),上面的结构体将像这样布局:

|   1   |   2   | 

| AA(1) | pad.. |
| BB(1) | BB(2) |
| BB(3) | BB(4) |
| CC(1) | pad.. |

sizeof(Test)是2 × 4 = 8。

struct中变量的顺序也很重要。变量顺序如下:

struct Test
{
   char AA;
   char CC;
   int BB;
};

使用#pragma pack(2),结构体将像这样布局:

|   1   |   2   | 

| AA(1) | CC(1) |
| BB(1) | BB(2) |
| BB(3) | BB(4) |

sizeOf(Test)为3 × 2 = 6。

其他回答

你可能只在对某些硬件(例如内存映射设备)编码时才会使用它,这些硬件对寄存器排序和对齐有严格的要求。

然而,这似乎是实现这一目标的一个相当生硬的工具。一个更好的方法是在汇编程序中编写一个迷你驱动程序,并给它一个C调用接口,而不是摸索这个pragma。

它告诉编译器要将结构中的对象对齐到的边界。例如,如果我有这样的东西:

struct foo { 
    char a;
    int b;
};

对于典型的32位机器,您通常“希望”在a和b之间有3个字节的填充,以便b将在4字节的边界上着陆,以最大限度地提高其访问速度(这通常是默认情况)。

然而,如果你必须匹配一个外部定义的结构,你想要确保编译器完全根据外部定义来布局你的结构。在这种情况下,你可以给编译器一个#pragma包(1)来告诉它不要在成员之间插入任何填充——如果结构的定义包含了成员之间的填充,你可以显式地插入它(例如,典型的成员名为unusedN或ignoreN,或者类似的顺序)。

编译器可以对结构中的成员进行对齐,以在特定平台上实现最大性能。#pragma包指令允许你控制对齐方式。通常,您应该将其保留为默认值以获得最佳性能。如果你需要传递一个结构到远程机器,你通常会使用#pragma pack 1来排除任何不需要的对齐。

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

struct Test
{
   char AA;
   int BB;
   char CC;
};

编译器可以选择像这样在内存中布局结构:

|   1   |   2   |   3   |   4   |  

| AA(1) | pad.................. |
| BB(1) | BB(2) | BB(3) | BB(4) | 
| CC(1) | pad.................. |

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

|   1   |

| AA(1) |
| BB(1) |
| BB(2) |
| BB(3) |
| BB(4) |
| CC(1) |

sizeof(Test)是1 × 6 = 6。

使用#pragma pack(2),上面的结构体将像这样布局:

|   1   |   2   | 

| AA(1) | pad.. |
| BB(1) | BB(2) |
| BB(3) | BB(4) |
| CC(1) | pad.. |

sizeof(Test)是2 × 4 = 8。

struct中变量的顺序也很重要。变量顺序如下:

struct Test
{
   char AA;
   char CC;
   int BB;
};

使用#pragma pack(2),结构体将像这样布局:

|   1   |   2   | 

| AA(1) | CC(1) |
| BB(1) | BB(2) |
| BB(3) | BB(4) |

sizeOf(Test)为3 × 2 = 6。

#pragma用于向编译器发送不可移植(仅在此编译器中)消息。像禁用某些警告和包装结构是常见的原因。如果在编译时将警告作为错误标志打开,则禁用特定警告特别有用。

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

使用#pragma pack(push,1)和#pragma pack(pop)来撤销换行