The alignment of a structure with two doubles is 4 even though double is aligned to 8 (32 bits)

I am on an x86_64 machine, with Ubuntu 16.04 and gcc 5.4.0.

I ran into this somewhat strange behavior today.

$ cat main.c
#include <stdio.h>
struct dd { double d1; double d2; };
int main()
{
    printf("%d\n", (int)__alignof__(double));
    printf("%d\n", (int)__alignof__(struct dd));
}
$ gcc -m32 -o main main.c && ./main
8
4

      

__alignof__

returns 8 for double

and 4 for struct dd

. I think it's strange to struct

be aligned in a smaller block than its members. This behavior even contradicts the ISO C standard as specified in the gcc 5.4.0 doc :

Note that the alignment of any given type of struct

or union

is required by the ISO C standard to be at least a perfect multiple of the lowest common multiple of the alignments of all members of struct

or union

.

What is the reason for this behavior? Is this struct

4 byte optimized way to access memory compatible ?

+3


source to share


1 answer


A document defining how this should behave in your architecture is here at i386 System V psABI . (The current version is here , see also the x86 tag wiki). In it we can read that the required alignment of double is 4. But this one has an interesting note:

Intel386 architecture does not require double alignment for double precision values. However, for compatibility of the data structure with other Intel architectures, compilers may provide a method to align double precision values ​​at double word boundaries.

A compiler that provides the doubleword alignment mentioned above may generate code (data structures and function call sequences) that is not AB38 Intel386 compatible. Programs created with double-word alignment Thus, the object may violate Intel386 ABI compliance. See Aggregates and Unions below, and Function Call Sequence later in this chapter for more information.

GCC doesn't want to break the ABI for structs (where alignment is very relevant), so it correctly uses 4 alignment for paired elements within structs.

This behavior even contradicts the ISO C standard

ISO C is completely irrelevant here, as __alignof__

it is not part of any C standard. The compiler might have done something, like get an image of a cat from the Internet and show it to you, and that would be behavior that is fully compliant with the C standard.

C11 indicates, however, an operator _Alignof

. Interestingly, if we use an operator _Alignof

that is part of the C11 standard, the GCC reports other (correct) numbers:

$ cat foo.c
#include <stdio.h>
struct dd { double d1; double d2; };
int main()
{
    printf("%d\n", (int)__alignof__(double));
    printf("%d\n", (int)__alignof__(struct dd));
}
$ cc -m32 -o foo foo.c && ./foo
8
4
$ ed foo.c
[...]
$ cat foo.c
#include <stdio.h>
struct dd { double d1; double d2; };
int main()
{
    printf("%d\n", (int)_Alignof(double));
    printf("%d\n", (int)_Alignof(struct dd));
}
$ cc -m32 -o foo foo.c && ./foo
4
4

      


The wording of the C standard is not very specific about what should happen in the ABI, where types inside structures have lower alignment than outside.

After careful study of the standard wording and some debate, the gcc developers decided that they _Alignof

should tell you the minimum alignment you'll ever see a value of this type in a strict C11 program ( https://gcc.gnu.org/ml/gcc-patches/2013 -12 / msg00435.html ). (This is what you want to use, for example, to write a garbage collector that scans blocks of memory for potential pointers.) Note that C11 does not include __attribute__((packed))

, and the casting of unaligned pointers is UB.

This mailing list explains why they changed C11 _Alignof

, but not C ++ alignof

or the GNU extension __alignof__

.



GNU C __alignof__

goes on to mean how gcc will align this type as global or local, out of structure. that is, the maximum / recommended alignment. The current version of the i386 SysV ABI says nothing about alignment double

to 8B; this is purely optional behavior of modern compilers for performance.

The presence _Alignof(double) <= _Align(struct containing_double)

appears to meet all the requirements of the C11 standard, although the preferred alignment for double

is 8B. double

works when crossing border 4B, it is just slow if it crosses a cache line or page.

(But note that _Atomic long long

doesn't work if it's not 8B aligned, so clang gives it 8B alignment even inside structs . Current gcc will be split for C11 stdatomic 8B types on 32-bit SysV ABI and hopefully change to match clang.)


In clang, _Alignof

it seems the same as __alignof__

. So it disagrees with gcc about the C11 operator (but not about the structure of the struct other than the C11 stdatomic).

See some test cases in the Godbolt compiler explorer with gcc7.2 and clang4.0. Remove -xc

to compile as C ++ instead of C


somewhat related: gcc7 increased alignment max_align_t

in 32-bit from 8 to 16, for_Float128

, but malloc(8)

or strdup("abc")

can only return 8B-aligned blocks.

gcc stddef.h

implementsmax_align_t

with a structure with members like

long long __max_align_ll __attribute__((__aligned__(__alignof__(long long))));

      

to make sure that the resulting structure does indeed have as many alignment ( _Alignas

) requirements as its members. It also has members long double

and __float128

.

+4


source







All Articles