18

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?

psaw.mora
  • 868
  • 1
  • 7
  • 18
  • Using `#pragma pack` is pretty rare - and you potentially give up portability of your code as packing is system dependent. – wkl Oct 19 '11 at 14:28
  • 2
    A better question would be: "When would it be *appropriate* to use **#pragma pack** ?". (Answer: very rarely, and only if you know what you're doing.) – Paul R Oct 19 '11 at 14:31
  • 1
    A possible reason to use pack(1) would be for data structures that should be spit directly into the net without any realignment . That for example a struct with a character and a 16bit integer really is only 3bytes long. – alk Oct 19 '11 at 14:54
  • @alk: Even then, you have to worry about byte order. It's much better just to write it as `unsigned char[3]`. – R.. GitHub STOP HELPING ICE Oct 20 '11 at 03:34

5 Answers5

44

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.

Andrew Cottrell
  • 3,312
  • 3
  • 26
  • 41
  • 3
    -1 for "You should use #pragma pack". There are plenty of other ways to handle this situation which are actually valid C. – R.. GitHub STOP HELPING ICE Oct 20 '11 at 03:33
  • 20
    R.. can you give a few examples of "plenty of other ways to handle this situation"? I have also always used #pragma pack just how Andrew suggests. Thanks. – Brian Larsen Oct 01 '12 at 16:48
  • 7
    In my experience with embedded platforms and network communication, I've found #pragma pack to be exceedingly useful. If you know how to properly order and align your structure for your given architecture (and compiler), there should not be a performance degradation. – pattivacek Jun 19 '13 at 18:26
3

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.

Mysticial
  • 464,885
  • 45
  • 335
  • 332
  • Not just performance consequences - misaligned accesses for certain data types result in an exception (i.e. crash) on many architectures. – Paul R Oct 19 '11 at 14:30
  • Indeed. SSE/AVX instructions will result in exceptions. (unless you're using the misaligned load/stores) – Mysticial Oct 19 '11 at 14:32
1

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.

Himanshu
  • 31,810
  • 31
  • 111
  • 133
lalit
  • 11
  • 1
1

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.

Lindydancer
  • 25,428
  • 4
  • 49
  • 68
-1

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.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 1
    Downvoter, care to explain? I believe I answered the question and offered useful additional information on why. – R.. GitHub STOP HELPING ICE Oct 20 '11 at 03:32
  • 7
    Just stumbled onto this thread. I didn't downvote you, but I hesitate to upvote. The reason is, you said "should never use". But I use `#pragma pack(push, 4)` quite a lot in code that I need to be portable between ARM and x64, so I disagree with the assertion that `#pragma pack` should never be used. IMHO, whether or not you should use it depends on *how* portable you need your code to be. – Todd Lehman Apr 15 '15 at 21:34
  • @ToddLehman: I don't see how `#pragma pack` can help you achieve "portability between ARM and x64". – R.. GitHub STOP HELPING ICE Apr 16 '15 at 03:10
  • 2
    — It doesn't help me achieve portability per se. It's just that I am able to use it on code that I need to be portable between ARM and x64, without losing any portability. That is, it doesn't hurt portability in the cases I'm using it. For example, I use `#pragma pack(push, 4)` liberally in `structs` containing both a `uint64_t` and a `uint32_t`, so that it's always a multiple of 4 bytes instead of 8. I love `pragma pack` and I highly recommend using it in limited situations. Even on x64, it's faster to loop over a large table of 12-byte structs than 16-byte structs. – Todd Lehman Apr 17 '15 at 00:19
  • @ToddLehman: 4 years after he asked for clarification, and you are sure you would even swear you were not the downvoter at that time? * sarcasticaly* :D – dhein Jul 16 '15 at 09:00
  • I use `#pragma pack` in this use case: a single c# dll for all platforms that define a struct that is passed to native code. The struct in C# enforces a specific packing with attribute `StructLayout`. Without `#pragma pack` on the native side I can't ensure structs can be safely blitted on all platforms. So your argument "It always results in dangerous portability problems" is plain wrong. – ceztko Apr 11 '18 at 08:47