'struct' member alignment query

Our architecture has fairly conventional data layout rules, with:

  o 8-bit, byte aligned 'char' (int8_t)
  o 16-bit, 2-byte aligned 'short' (int16_t)
  o 32-bit, 4-byte aligned 'int' and 'long' (int32_t)
  o 64-bit, 8-byte aligned 'long long' (int64_t)

However, I wanted to experiment with 'struct's that internally have
self-consistent member alignment, but which deliberately map a 'struct'
member of another 'struct' onto a boundary that is not consistent with the
member 'struct's desired alignment.

The test I am using is appended (it is a C file), and the line in this that
I have a question about is #21:

    AlignCheck_t alignCheck2 __attribute__((aligned(1))); // Should have an
alignment of 1-Byte???

where I have tried to override the default alignment for the aggregate
member, but the '__attribute__((aligned(1)))' does not appear to have any
effect and the member is padded in the normal way, and so fails with the
test at line #64.

Is this expected behaviour in 'clang', and if so, is there another way I can
coerce the 'struct' member 'alignCheck2' onto another less constrained
alignment boundary?

Thanks,

  MartinO

---------- alignmentCheck.c ----------
#include <stdio.h>
#include <stddef.h>

typedef struct AlignCheck_t {
   char a; // 0: [a]
                    // 1: [X-X-X] padding
   int b; // 4: [b-b-b-b]
   char c; // 8: [c]
                    // 9: padding
   short d; // 10: [d-d]
   char e; // 12: [e]
                    // 13: [X-X-X] padding
   long long f[2]; // 16: [f-f-f-f-f-f-f-f][f-f-f-f-f-f-f-f]
} AlignCheck_t;

typedef struct Group_t {
    char dummy1; // Should have an
alignment of 1-Byte
    AlignCheck_t alignCheck1; // Should have an
alignment of 8-Bytes

    char dummy2; // Should have an
alignment of 1-Byte
    AlignCheck_t alignCheck2 __attribute__((aligned(1))); // Should have an
alignment of 1-Byte???

    char dummy3; // Should have an
alignment of 1-Byte
} Group_t;

char dummy0; // Should have an
alignment of 1-Byte
Group_t alignedGroup = {
                            1,
                           { 2, 3, 4, 5, 6, 7ll },
                           11,
                           { 12, 13, 14, 15, 16, 17ll },
                           21
                       };

int main( void )
{
    int failed = 0;
    unsigned check1 = (unsigned)&alignedGroup.dummy1;
    unsigned check2 = (unsigned)&alignedGroup.alignCheck1;
    unsigned check3 = (unsigned)&alignedGroup.dummy2;
    unsigned check4 = (unsigned)&alignedGroup.alignCheck2;
    unsigned check5 = (unsigned)&alignedGroup.dummy3;

    if (( check2 & 0x00000007 ) != 0 )
        failed = 1;
    else if ( offsetof ( AlignCheck_t, a ) != 0 )
        failed = 2;
    else if ( offsetof ( AlignCheck_t, b ) != 4 )
        failed = 3;
    else if ( offsetof ( AlignCheck_t, c ) != 8 )
        failed = 4;
    else if ( offsetof ( AlignCheck_t, d ) != 10 )
        failed = 5;
    else if ( offsetof ( AlignCheck_t, e ) != 12 )
        failed = 6;
    else if ( offsetof ( AlignCheck_t, f ) != 16 )
        failed = 7;
    else if ( sizeof ( AlignCheck_t ) != 32 )
        failed = 8;
    else if (( check2 - check1 ) != 8 )
        failed = 9;
    else if (( check3 - check2 ) != 32 )
        failed = 10;
    else if (( check4 - check3 ) != 1 )
        failed = 11;
    else if (( check5 - check4 ) != 32 )
        failed = 12;

    if ( failed )
        puts("\n\nAlignment check FAILED\n\n");
    else
        puts("\n\nAlignment check PASSED\n\n");

    return failed;
}

It's expected, but I think only because of GCC compatibility because
it's really not obvious behaviour. The workaround, which still
mystifies me, is to go via a typedef:

typedef AlignCheck_t __attribute__((aligned(1))) UnAlignCheck_t;

then you can put that in the struct and it'll be laid out without any padding.

Tim.

Wow! Thanks for the response, that worked perfectly.

I never would have expected that the indirect 'typedef' approach would behave any differently to the more direct annotation! I feel that this is probably an unintended GCC behaviour/bug, though CLang is 100% right to maintain compatibility with GCC.

But it solved my problem, thanks very much,

  MartinO

The “right” way to get this effect is to use attribute((packed)) on the field.

Hi Richard,

How does ‘packed’ work in this context? I thought ‘packed’ would cause the whole aggregate to eliminate padding, rather than just changing the alignment requirements of a single member.

What I want is that the outer aggregate is aligned in the normal way according the maximum alignment requirements of its members. However, I want to also have a member which itself is an aggregate with its usual internal padding retained, but changed its alignment constraint (essentially lying) within the context of the enclosing aggregate.

Would ‘packed’ not just unilaterally remove the padding?

For example (assuming char is 1-byte and int is 4, and natural alignment):

struct Inner {

char c;

int i;

};

Inner x;

I can reasonably assume on a naturally aligned system that the following would hold true (undefined ISO behaviour ignored regarding address arithmetic - assume a simple conventional 32-bit Von Neumann address space):

sizeof(Inner) == 8

((unsigned)&x & 7) == 0

((unsigned)&x.i - (unsigned)&x.c) == 4

offsetof(Outer, c) == 0

offsetof(Outer, i) == 4

Now what I want to do is:

struct Outer {

char aa;

int bb;

char cc;

[magic attribute] Inner xx;

char dd;

int ee;

};

Outer y;

Such that:

sizeof(Outer) == 24

((unsigned)&y & 7) == 0

((unsigned)&y.xx.i - (unsigned)&y.xx.c) == 4

((unsigned)&y.xx.i - (unsigned)&y) == 13

offsetof(Inner, bb) == 4

offsetof(Inner, cc) == 8

offsetof(Inner, xx) == 9

offsetof(Inner, dd) == 17

offsetof(Inner, ee) == 20

so padding is still present within the layout of ‘xx’, and also as normal for the member alignment of ‘aa’, ‘bb’, ‘cc’, ‘dd’ and ‘ee’ within ‘Outer’, but not for ‘xx’.

This is useful when overlaying structures on registers in a memory-mapped device which can have weird arrangements, and avoid the need for dirty little macro arithmetic tricks which are sadly very common in embedded systems device driver code.

So forgive me if I am misunderstanding ‘packed’ (and I probably am), but will this not cause the members of ‘Inner’ as a member of ‘Outer’ to become packed ((unsigned)&i == (unsigned)&c + 1), or does it simply loosen the alignment constraint within the members of ‘Outer’ to achieve what I intend? I usually interpret ‘packed’ as all-or-nothing. So if I replace ‘[magic attribute]’ with ‘attribute((packed))’, this will achieve the effect I require? What if I want to force alignment of ‘xx’ to 2-bytes instead of 1?

The only reasonably portable way I know of doing this kind of thing, is to always pack structures, and to explicitly provide the dummy padding bytes (including bitfield alignment magic such as ‘int:0;’), but while these are mostly-portable, they are not absolute in the context of a specific implementation - which is where ‘attribute((aligned(x)))’ is necessary.

In any case, the incongruity of using ‘aligned’ in a ‘typedef’ versus explicitly on the member is quite unintuitive, though I fully understand the need for CLang to maintain compatibility with GCC in this regard.

Thanks,

MartinO

I can verify that this does work though, even if I don’t understand the semantics or rationale.

Thanks,

MartinO

What packed actually does is to reduce the minimum alignment to 1. When applied to a struct, it is effectively applied to all fields.