When should we not use #pragma pack?

2019-02-04 12:24发布

问题:

In C, when we use structures, when would it be inappropriate to use #pragma pack directive..?

an addition to the question.....

Can someone please explain more on how might the accessing of unaligned data specially with a pointer fail?

回答1:

Firmware developer here. #pragma pack is very familiar territory. I'll explain.

In general you should not use #pragma pack. Yes, it will make your structures smaller in memory since it eliminates all padding between struct members. But it can make accessing those members much more expensive since the members may no longer fall along their required alignment. For example, in ARM architectures, 4-byte ints are typically required to be 4-byte aligned, but in a packed struct they might not be. That means the compiler needs to add extra instructions to safely access that struct member, or the developer has to access it byte-by-byte and reconstruct the int manually. Either way it results in more code than an aligned access, so your struct ends up smaller but your accessing code potentially ends up slower and larger.

You should use #pragma pack when your structure must match an exact data layout. This typically happens when you are writing code to match a data transport or access specification... e.g., network protocols, storage protocols, device drivers that access HW registers. In those cases you may need #pragma pack to force your structures to match the spec-defined data layout. This will possibly incur the same performance penalty mentioned in the previous paragraph, but may be the only way to comply with the specification.



回答2:

I would say that you shouldn't pack unless there's a really good reason to do so.

When pack is specified, all the padding is stripped out. Therefore the struct members could be unaligned - which could have performance consequences.



回答3:

In most architecture, the underlying access must match the alignment of the accessed data.

This mean, that if you have a 32 bit value, you can access it efficiently if it is stored at an address that is dividable by four.

If you use #pragma pack, the location of the variable can be anything, and the compiler must access the element piece by piece and combine them together. Concretely, below is the generated code to read a normal int on a V850E (a popular microcontoller in the embedded world):

LD.W        a[zero],r5

Correspondingly, the following is the code to access an int in a packed structure:

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

Another reason not to use packed structs is that it's not possible to dereference a pointer to a member of a packed struct, unless the architecture support unaligned pointer accesses. The reason for this is that type of the point will be a plain int pointer, and the compiler has no knowledge that it must access whatever it points to piece-by-piece.

I would strongly recommend that you don't use '#pragma pack' at all, unless it's absolutely necessary. If you have control over the struct definition, there are techniques to make sure the struct layout is padding-free. If not, a better approach would be to copy any unaligned data to a new, aligned, struct and use it in your application.



回答4:

The #pragma pack directive offers a way to fulfill this requirement. This directive specifies packing alignment for structure members. The pragma takes effect at the first structure declaration after the pragma is seen. Turbo C/C++ compiler doesn’t support this feature, VC++ compiler does.



回答5:

You should never use #pragma pack or similar. It always results in dangerous portability problems. For instance, consider the struct:

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

and call scanf("%d", &bar.b). On machines without misaligned access, this will fault (or corrupt memory!) inside scanf! This is because &bar.b is not actually a valid int * - it's misaligned, but the code it's passed to can't know that and work around it like the compiler would if you had just written bar.b = 42;.

The only intended use for packed structures is serialization, but that also results in non-portable files. You should simply write proper serialization functions.



标签: c structure