简体   繁体   English

#pragma 包效果

[英]#pragma pack effect

I was wondering if someone could explain to me what the #pragma pack preprocessor statement does, and more importantly, why one would want to use it.我想知道是否有人可以向我解释#pragma pack预处理器语句的作用,更重要的是,为什么要使用它。

I checked out the MSDN page , which offered some insight, but I was hoping to hear more from people with experience.我查看了MSDN 页面,该页面提供了一些见解,但我希望从有经验的人那里听到更多。 I've seen it in code before, though I can't seem to find where anymore.我以前在代码中见过它,但我似乎再也找不到在哪里了。

#pragma pack instructs the compiler to pack structure members with particular alignment. #pragma pack指示编译器以特定对齐方式打包结构成员。 Most compilers, when you declare a struct, will insert padding between members to ensure that they are aligned to appropriate addresses in memory (usually a multiple of the type's size).大多数编译器,当你声明一个结构体时,会在成员之间插入填充以确保它们与内存中的适当地址对齐(通常是类型大小的倍数)。 This avoids the performance penalty (or outright error) on some architectures associated with accessing variables that are not aligned properly.这避免了与访问未正确对齐的变量相关的某些体系结构上的性能损失(或完全错误)。 For example, given 4-byte integers and the following struct:例如,给定 4 字节整数和以下结构:

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

The compiler could choose to lay the struct out in memory like this:编译器可以选择将结构放在内存中,如下所示:

|   1   |   2   |   3   |   4   |  

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

and sizeof(Test) would be 4 × 3 = 12, even though it only contains 6 bytes of data. sizeof(Test)将是 4 × 3 = 12,即使它只包含 6 个字节的数据。 The most common use case for the #pragma (to my knowledge) is when working with hardware devices where you need to ensure that the compiler does not insert padding into the data and each member follows the previous one. #pragma最常见的用例(据我所知)是在使用硬件设备时,您需要确保编译器不会将填充插入数据并且每个成员都遵循前一个。 With #pragma pack(1) , the struct above would be laid out like this:使用#pragma pack(1) ,上面的结构将如下所示:

|   1   |

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

And sizeof(Test) would be 1 × 6 = 6.sizeof(Test)将是 1 × 6 = 6。

With #pragma pack(2) , the struct above would be laid out like this:使用#pragma pack(2) ,上面的结构将如下所示:

|   1   |   2   | 

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

And sizeof(Test) would be 2 × 4 = 8.sizeof(Test)将是 2 × 4 = 8。

Order of variables in struct is also important. struct 中变量的顺序也很重要。 With variables ordered like following:变量排序如下:

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

and with #pragma pack(2) , the struct would be laid out like this:使用#pragma pack(2) ,结构体将如下所示:

|   1   |   2   | 

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

and sizeOf(Test) would be 3 × 2 = 6.sizeOf(Test)将是 3 × 2 = 6。

#pragma is used to send non-portable (as in this compiler only) messages to the compiler. #pragma用于向编译器发送不可移植(仅在此编译器中)消息。 Things like disabling certain warnings and packing structs are common reasons.禁用某些警告和打包结构之类的事情是常见的原因。 Disabling specific warnings is particularly useful if you compile with the warnings as errors flag turned on.如果在错误标志打开时使用警告进行编译,则禁用特定警告特别有用。

#pragma pack specifically is used to indicate that the struct being packed should not have its members aligned. #pragma pack专门用于指示被打包的结构不应使其成员对齐。 It's useful when you have a memory mapped interface to a piece of hardware and need to be able to control exactly where the different struct members point.当您有一个硬件的内存映射接口并且需要能够准确控制不同结构成员指向的位置时,它很有用。 It is notably not a good speed optimization, since most machines are much faster at dealing with aligned data.这显然不是一个好的速度优化,因为大多数机器在处理对齐数据时要快得多。

To undo afterwards wrap in #pragma pack(push,1) and #pragma pack(pop)之后撤消包装在#pragma pack(push,1)#pragma pack(pop)

It tells the compiler the boundary to align objects in a structure to.它告诉编译器将结构中的对象对齐到的边界。 For example, if I have something like:例如,如果我有类似的东西:

struct foo { 
    char a;
    int b;
};

With a typical 32-bit machine, you'd normally "want" to have 3 bytes of padding between a and b so that b will land at a 4-byte boundary to maximize its access speed (and that's what will typically happen by default).对于典型的 32 位机器,您通常“希望”在ab之间有 3 个字节的填充,以便b将落在 4 个字节的边界上以最大化其访问速度(这就是默认情况下通常会发生的情况)。

If, however, you have to match an externally defined structure you want to ensure the compiler lays out your structure exactly according to that external definition.但是,如果您必须匹配外部定义的结构,您希望确保编译器完全根据该外部定义布置您的结构。 In this case, you can give the compiler a #pragma pack(1) to tell it not to insert any padding between members -- if the definition of the structure includes padding between members, you insert it explicitly (eg, typically with members named unusedN or ignoreN , or something on that order).在这种情况下,您可以给编译器一个#pragma pack(1)以告诉它不要在成员之间插入任何填充——如果结构的定义包括成员之间的填充,则显式插入它(例如,通常具有名为unusedNignoreN ,或该顺序的东西)。

Data elements (eg members of classes and structs) are typically aligned on WORD or DWORD boundaries for current generation processors in order to improve access times.数据元素(例如类和结构的成员)通常在当前一代处理器的 WORD 或 DWORD 边界上对齐,以缩短访问时间。 Retrieving a DWORD at an address which isn't divisible by 4 requires at least one extra CPU cycle on a 32 bit processor.在不能被 4 整除的地址处检索 DWORD 需要在 32 位处理器上至少增加一个 CPU 周期。 So, if you have eg three char members char a, b, c;所以,如果你有三个 char 成员char a, b, c; , they actually tend to take 6 or 12 bytes of storage. ,它们实际上往往需要 6 或 12 个字节的存储空间。

#pragma allows you to override this to achieve more efficient space usage, at the expense of access speed, or for consistency of stored data between different compiler targets. #pragma允许您覆盖它以实现更有效的空间使用,代价是访问速度,或不同编译器目标之间存储数据的一致性。 I had a lot of fun with this transitioning from 16 bit to 32 bit code;从 16 位代码到 32 位代码的转换给我带来了很多乐趣; I expect porting to 64 bit code will cause the same kinds of headaches for some code.我希望移植到 64 位代码会对某些代码造成同样的麻烦。

Compiler could align members in structures to achieve maximum performance on the certain platform.编译器可以对齐结构中的成员以在特定平台上实现最大性能。 #pragma pack directive allows you to control that alignment. #pragma pack指令允许您控制该对齐方式。 Usually you should leave it by default for optimum performance.通常您应该默认保留它以获得最佳性能。 If you need to pass a structure to the remote machine you generally will use #pragma pack 1 to exclude any unwanted alignment.如果您需要将结构传递给远程机器,您通常会使用#pragma pack 1来排除任何不需要的对齐。

A compiler may place structure members on particular byte boundaries for reasons of performance on a particular architecture.出于特定体系结构上的性能原因,编译器可以将结构成员放置在特定字节边界上。 This may leave unused padding between members.这可能会在成员之间留下未使用的填充。 Structure packing forces members to be contiguous.结构填充强制成员是连续的。

This may be important for example if you require a structure to conform to a particular file or communications format where the data you need the data to be at specific positions within a sequence.这可能很重要,例如,如果您需要一个结构符合特定文件或通信格式,其中您需要数据位于序列中的特定位置。 However such usage does not deal with endian-ness issues, so although used, it may not be portable.然而,这种用法不处理字节序问题,所以虽然使用了它,但它可能无法移植。

It may also to exactly overlay the internal register structure of some I/O device such as a UART or USB controller for example, in order that register access be through a structure rather than direct addresses.它也可以精确地覆盖一些 I/O 设备(例如 UART 或 USB 控制器)的内部寄存器结构,以便通过结构而不是直接地址访问寄存器。

I have seen people use it to make sure that a structure takes a whole cache line to prevent false sharing in a multithreaded context.我见过人们使用它来确保结构采用整个缓存行以防止多线程上下文中的错误共享。 If you are going to have a large number of objects that are going to be loosely packed by default it could save memory and improve cache performance to pack them tighter, though unaligned memory access will usually slow things down so there might be a downside.如果您将拥有大量默认松散打包的对象,它可以节省内存并提高缓存性能以将它们打包得更紧,尽管未对齐的内存访问通常会减慢速度,因此可能会有不利影响。

You'd likely only want to use this if you were coding to some hardware (eg a memory mapped device) which had strict requirements for register ordering and alignment.如果您正在对某些对寄存器排序和对齐有严格要求的硬件(例如内存映射设备)进行编码,您可能只想使用它。

However, this looks like a pretty blunt tool to achieve that end.然而,这看起来是实现这一目标的一个非常生硬的工具。 A better approach would be to code a mini-driver in assembler and give it a C calling interface rather than fumbling around with this pragma.更好的方法是在汇编程序中编写一个微型驱动程序并为其提供 C 调用接口,而不是摸索这个 pragma。

I've used it in code before, though only to interface with legacy code.我以前在代码中使用过它,但只是为了与遗留代码交互。 This was a Mac OS X Cocoa application that needed to load preference files from an earlier, Carbon version (which was itself backwards-compatible with the original M68k System 6.5 version...you get the idea).这是一个 Mac OS X Cocoa 应用程序,需要从早期的 Carbon 版本(它本身向后兼容原始 M68k System 6.5 版本......你懂的)加载首选项文件。 The preference files in the original version were a binary dump of a configuration structure, that used the #pragma pack(1) to avoid taking up extra space and saving junk (ie the padding bytes that would otherwise be in the structure).原始版本中的首选项文件是一个配置结构的二进制转储,它使用#pragma pack(1)来避免占用额外的空间和保存垃圾(即结构中本来会存在的填充字节)。

The original authors of the code had also used #pragma pack(1) to store structures that were used as messages in inter-process communication.代码的原始作者还使用#pragma pack(1)来存储在进程间通信中用作消息的结构。 I think the reason here was to avoid the possibility of unknown or changed padding sizes, as the code sometimes looked at a specific portion of the message struct by counting a number of bytes in from the start (ewww).我认为这里的原因是为了避免出现未知或更改填充大小的可能性,因为代码有时会通过从头开始计算字节数(ewww)来查看消息结构的特定部分。

Note that there are other ways of achieving data consistency that #pragma pack offers (for instance some people use #pragma pack(1) for structures that should be sent across the network).请注意,#pragma pack 提供了其他实现数据一致性的方法(例如,有些人使用 #pragma pack(1) 来表示应该通过网络发送的结构)。 For instance, see the following code and its subsequent output:例如,请参阅以下代码及其后续输出:

#include <stdio.h>

struct a {
    char one;
    char two[2];
    char eight[8];
    char four[4];
};

struct b { 
    char one;
    short two;
    long int eight;
    int four;
};

int main(int argc, char** argv) {
    struct a twoa[2] = {}; 
    struct b twob[2] = {}; 
    printf("sizeof(struct a): %i, sizeof(struct b): %i\n", sizeof(struct a), sizeof(struct b));
    printf("sizeof(twoa): %i, sizeof(twob): %i\n", sizeof(twoa), sizeof(twob));
}

The output is as follows: sizeof(struct a): 15, sizeof(struct b): 24 sizeof(twoa): 30, sizeof(twob): 48输出如下: sizeof(struct a): 15, sizeof(struct b): 24 sizeof(twoa): 30, sizeof(twob): 48

Notice how the size of struct a is exactly what the byte count is, but struct b has padding added (see this for details on the padding).请注意一个结构的大小是如何准确的字节数是什么,但结构B已经添加填充(见关于填充细节)。 By doing this as opposed to the #pragma pack you can have control of converting the "wire format" into the appropriate types.通过这样做而不是#pragma 包,您可以控制将“有线格式”转换为适当的类型。 For instance, "char two[2]" into a "short int" et cetera.例如,将“char two[2]”转换为“short int”等等。

Why one want to use it ?为什么要使用它?

To reduce the memory of the structure减少结构体的内存

Why one should not use it ?为什么不应该使用它?

  1. This may lead to performance penalty, because some system works better on aligned data这可能会导致性能下降,因为某些系统在对齐数据上效果更好
  2. Some machine will fail to read unaligned data某些机器将无法读取未对齐的数据
  3. Code is not portable代码不可移植

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM