Do C struct members inherit compiler attributes?

Viewed 137

With most C compilers, one can specify compiler attributes on a struct that define how the members of that struct are aligned in memory. Ex:

typedef struct{
    char a;
    char b;
} __attribute__((aligned(2))) TwoChars;

If char a ends up at the address 0xA (for simplicity), then char b will not be at address 0xB, but 0xC, because it's aligned to 2 bytes.

My question is: is this property inherited by struct members? Ex:

typedef struct{
    char a;
    char b;
} TwoChars;

typedef struct {
    TwoChars tc;
    char c;
} __attribute__((aligned(1))) ThreeChars;

What does this end up looking like in memory? How about with } __attribute__((aligned(2))) TwoChars?

2 Answers

Note: this is a non-standard compiler-specific extension. What I mention in my answer may be applied to GCC and Clang, which I assume is what you mean by "most C compilers".


one can specify compiler attributes on a struct that define how the members of that struct are aligned in memory

No, that's not how __attribute__((aligned(...))) works. The aligned attribute only applies to the structure itself, not its fields. See the relative documentation page.

If you want to control the alignment of structure fields, you will have to use the attribute on them.

This:

struct {
    char a;
    char b;
} __attribute__((aligned(8))) foo = { 1, 2 };

Will compile to something like:

foo:
    .byte   1
    .byte   2
    .zero   6

While this:

struct {
    char a __attribute__((aligned(2)));
    char b __attribute__((aligned(2)));
} bar = { 1, 2 };

Will do what you want:

bar:
    .byte   1
    .zero   1
    .byte   2
    .zero   1

Therefore:

is this property inherited by struct members?

No. Alignment is not something that is inherited by structure members.


An interesting thing to know would be if the packed attribute is inherited by members of nested structures inside the structure on which it's applied, since packed is specifically meant to be inherited by structure members. Documentation does not explicitly state whether this is the case or not, but we can check this easily.

The following code:

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

struct bar {
    struct foo d;
    char e;
    int f;
} __attribute__((packed));

struct bar x = {{101, 102, 103}, 104, 105};

Compiles to:

x:
    .byte   101
    .zero   3
    .long   102
    .byte   103
    .zero   3
    .byte   104
    .long   105        

We can see from the above that packed only applies to the members of bar and is not inherited by the members of foo, whereas adding __attribute__((packed)) to foo produces the following:

x:
    .byte   101
    .long   102
    .byte   103
    .byte   104
    .long   105

In case you were wondering, this also applies to structures defined right inside other structures:

struct bar {
    struct {
        char a;
        int b;
        char c;
    } d;
    char e;
    int c;
} __attribute__((packed));

struct bar x = {{101, 102, 103}, 104, 105};

Produces:

x:
    .byte   101
    .zero   3
    .long   102
    .byte   103
    .zero   3
    .byte   104
    .long   105        

Whereas adding __attribute__((packed)) to d produces:

x:
    .byte   101
    .long   102
    .byte   103
    .byte   104
    .long   105

That'll work and make ThreeChars aligned at least to 2.

You can test it for yourself:

typedef struct{
    char a;
    char b;
} __attribute__((aligned(2))) TwoChars;


typedef struct {
    TwoChars tc;
    char c;
} __attribute__((aligned(1))) ThreeChars;

_Static_assert(_Alignof(ThreeChars) == 2,"");

https://gcc.godbolt.org/z/9bTzbs

or you can read the documentation of the aligned attribute at https://gcc.gnu.org/onlinedocs/gcc-3.3/gcc/Type-Attributes.html .

(Unfortunately, the standardized _Alignas isn't as convenient to use, as it will be a constraint violation to specify a lower alignment with it.)

Note that only alignment of the whole struct is specified here, so while TwoChars tc; will be two-aligned and therefore affect the alignment of ThreeChars, the subsequent members will not have a higher than natural alignment.

The latter fact can be verified by _Static_asserting that the offset of be is 1 (_Static_assert(offsetof(TwoChars,b)==1,"");), as natural alignment would suggest, and so it cannot be 2-aligned.

Related