0

Among its Common Type Attributes, GCC provides packed:

This attribute, attached to a struct [...] type definition, specifies that each of its members (other than zero-width bit-fields) is placed to minimize the memory required. This is equivalent to specifying the packed attribute on each of the members.

Furthermore, from the C18 Standard (§ 6.7.2.1, 15-17):

Within a structure object, the non-bit-field members [...] have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member [...], and vice versa. There may be unnamed padding within a structure object, but not at its beginning. [...] There may be unnamed padding at the end of a structure [...].

So, given that there is no padding at the beginning of a structure, using packed on its first member seems redundant. Reciprocally (and this is where my issue lies), using packed on all of its members apart from the first one seems unnecessarily complicated and equivalent to using packed on the structure itself (we make the assumption that all members are of integer type(s)).

However I have come multiple times across code similar to:

struct S {
    unsigned a;
    unsigned b __attribute__ ((__packed__));
    unsigned c __attribute__ ((__packed__));
    unsigned d __attribute__ ((__packed__));
};

and I am unable to figure out why the author preferred such a struggle over

struct S {
    unsigned a;
    unsigned b;
    unsigned c;
    unsigned d;
} __attribute__ ((__packed__));

Is it a matter of alignment of the structure itself that packed on the first member would alter? Is it coming from something other than padding and alignment that I haven't thought about (e.g. quirk in older versions of GCC)? On the other hand, if they are equivalent, how can I be sure of it, given that the documentation is IMHO of little help here?

Although it is not a valid proof that they are equivalent, I have tried compiling both structs for various architectures (x86, x86_64, Aarch64) and they always seemed to share the same layout.

PiCTo
  • 924
  • 1
  • 11
  • 23
  • The first member is guaranteed to be located at offset zero. – wildplasser Oct 23 '20 at 13:21
  • gcc non-standard extensions are just like standard C - they come with lots of useless features. There is no sound reason why you would just want to pack some of the members of a struct. Why some people insist on using useless features is a matter of psychology, not programming. – Lundin Oct 23 '20 at 13:22
  • Btw if you wish to experiment with this, use some smaller type like `short`. The mentioned architectures can read 32 bit aligned data, so there's no reason why there would be padding. – Lundin Oct 23 '20 at 13:24
  • Are all the members really the same type as shown? – Ian Abbott Oct 23 '20 at 16:21
  • @IanAbbott Yes; any reason why that would matter? – PiCTo Oct 23 '20 at 16:40
  • @PiCTo Only that adding the packed attribute to the members is unlikely to make any difference if they are all the same elementary type. – Ian Abbott Oct 23 '20 at 16:47

1 Answers1

3

If the first member is not packed, it still has its original alignment requirement, which may be greater than one byte, and therefore the structure has at least that alignment requirement, and this may also require padding at the end of the structure.

For:

struct S {
    unsigned a;
    unsigned b __attribute__ ((__packed__));
    unsigned c __attribute__ ((__packed__));
    unsigned d __attribute__ ((__packed__));
};

GCC 10.2 for x86-64 says _Alignof(struct S) is 4. Whereas, if a or the whole structure is marked __attribute__ ((__packed__));, it says the alignment is 1.

If we change the last member to char:

struct S {
    unsigned a;
    unsigned b __attribute__ ((__packed__));
    unsigned c __attribute__ ((__packed__));
    char d __attribute__ ((__packed__));
};

then GCC says its size is 16 bytes, showing it has added three bytes of padding. If either a or the whole structure is marked __attribute__ ((__packed__)), the size changes to 13.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Thanks for your answer; Interesting (and ironic?) to see that, in your second example, even if `d` is `packed`, it will be followed by padding because of alignment constraints on `S`, inherited from `a`! So that means "`packed` on all members except first" is equivalent to `__attribute__((packed, aligned(_Alignof(T))))` on the structure, where `T` is the type of its first member? Or is there another difference? – PiCTo Oct 23 '20 at 14:04
  • In gcc it simply **never** packs the struct if the attribute is not applied to the whole struct. Even if you apply to all members. Bug? Not implemented? – 0___________ Oct 23 '20 at 14:18
  • @P__J__: GCC does pack the structure if packed is applied to each member but not to the structure. [Here](https://godbolt.org/z/fP337q) we see such a structure that has size 11 bytes and alignment requirement 1. 11 just suffices for the bytes of the members (4+1+4+2) with no padding, so the structure is packed. – Eric Postpischil Oct 23 '20 at 14:28