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
orunion
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 ofstruct
orunion
.
What is the reason for this behavior? Is this struct
4 byte optimized way to access memory compatible ?
source to share
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
.
source to share