Does packing a structure affect sub-structures?

Assuming GCC (or Clang emulating GCC), you can find some relevant information at Structure Layout Pragmas, where it says that the presence of the push preserves the current packing state on a stack of states:

For compatibility with Microsoft Windows compilers, GCC supports a set of #pragma directives that change the maximum alignment of members of structures (other than zero-width bit-fields), unions, and classes subsequently defined. The n value below always is required to be a small power of two and specifies the new alignment in bytes.

  1. #pragma pack(n) simply sets the new alignment.
  2. #pragma pack() sets the alignment to the one that was in effect when compilation started (see also command-line option -fpack-struct[=n] see Code Gen Options).
  3. #pragma pack(push[,n]) pushes the current alignment setting on an internal stack and then optionally sets the new alignment.
  4. #pragma pack(pop) restores the alignment setting to the one saved at the top of the internal stack (and removes that stack entry). Note that #pragma pack([n]) does not influence this internal stack; thus it is possible to have #pragma pack(push) followed by multiple #pragma pack(n) instances and finalized by a single #pragma pack(pop).

Thus, the #pragma in the code added affects all subsequent structure definitions too, until countermanded by #pragma pack(pop). I'd be worried about that.

The documentation doesn't say what happens if you do #pragma pack(pop) when there is no state on the internal stack. Most likely, it falls back to the setting when compilation started.


According to GCC reference:

In the following example struct my_packed_struct's members are packed closely together, but the internal layout of its s member is not packed - to do that, struct my_unpacked_struct would need to be packed too.

  struct my_unpacked_struct
   {
      char c;
      int i;
   };

  struct my_packed_struct __attribute__ ((__packed__))
    {
       char c;
       int  i;
       struct my_unpacked_struct s;
    };

You may only specify this attribute on the definition of a enum, struct or union, not on a typedef which does not also define the enumerated type, structure or union.


From the relevant documentation:

pack takes effect at the first struct, union, or class declaration after the pragma is seen. pack has no effect on definitions.

header is a member definition, so it isn't affected.

it was declared as part of the outer declaration, would it be subject to the packing then?

Yes, as it would be a struct declaration.

Also, as Lightness Races in Orbit remarks in a comment, a more convincing wording can be found immediately before:

To pack a class is to place its members directly after each other in memory.

i.e. it says nothing about what those members themselves contain, which may be data and/or padding. The fact that (as explored above) packedness is attached to a type would seem to reinforce that


Still, the documentation is vague enough, so it's best to test that this interpretation is correct; both gcc and VC++ behave as expected. Not that I'm particularly surprised - anything different would break havoc in the type system (taking a pointer to a member of a packed structure would actually provide a pointer to something different than its type says1).

The general idea is: once you finish defining a struct, its binary layout is fixed, and any of its instantiations will conform to it, including subobjects of packed structures. The current #pragma pack value is considered only when defining new structures, and when doing so the binary layout of the members is a fixed black box.


Notes

  1. To be honest, this is a bit of an x86-centric view; machines with stronger alignment requirements would object that even pointers to layout-correct but misaligned structures aren't kosher: although the offsets of fields relative to the given pointer are correct, they aren't really pointers that can be used as they are.

    OTOH, given a pointer to an unaligned object you can always detect that it's unaligned and memcpy it to a correctly-aligned location, so it's not as bad as a hypothetical pointer to a packed object, whose layout is effectively unknown unless you happen to know the packing of its parent.


From what I have seen, it only applies to immediate structure.

Take a look at the snippet below:

#include <stdio.h>

struct /*__attribute__((__packed__))*/ struct_Inner {
    char a;
    int b;
    char c;
};

struct __attribute__((__packed__)) struct_Outer {
    char a;
    int b;
    char c;
    struct struct_Inner stInner;
};

int main() 
{
   struct struct_Inner oInner;
   struct struct_Outer oOuter;
   printf("\n%zu Bytes", sizeof(oInner));
   printf("\n%zu Bytes", sizeof(oOuter));
}

Prints:

12 Bytes
18 Bytes

When I pack the struct_Inner it prints:

6 Bytes
12 Bytes

This is when using GCC 7.2.0.

Again, this is not specific to C standard by any means (I just had to read), its more to do with what compilers do.

Is the BITMAPINFOHEADER "protected" from the packing by virtue of the fact it's almost certainly be declared earlier?

I guess yes. It would entirely depend upon the way BITMAPINFOHEADER is declared.