具有两个双打的结构的对齐方式为 4,即使双打对齐为 8(32 位)
Alignment of a struct with two doubles is 4 even though double is aligned to 8 (32bit)
我在 x86_64 机器上,Ubuntu 16.04 和 gcc 5.4.0。
我今天遇到了这种有点奇怪的行为。
$ 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 个 double
和 4 个 struct dd
。我认为 struct
以比其成员更小的单位对齐很奇怪。这种行为甚至违背了 gcc 5.4.0 doc:
中提到的 ISO C 标准
Note that the alignment of any given struct
or union
type is required by the ISO C standard to be at least a perfect multiple of the lowest common multiple of the alignments of all of the members of the struct
or union
in question.
造成这种行为的原因可能是什么?将此 struct
对齐到 4 字节是访问内存的优化方式吗?
指定它在您的体系结构上的行为方式的文档是 here, in the i386 System V psABI. (Current revision here, see also the x86 标签 wiki)。在其中我们可以读到 double 所需的对齐方式是 4。但是它有这个有趣的注释:
The Intel386 architecture does not require doubleword alignment for double-precision values. Nevertheless, for data structure compatibility with other Intel architectures, compilers may provide a method to align double-precision values on doubleword boundaries.
A compiler that provides the doubleword alignment mentioned above can
generate code (data structures and function calling sequences) that do not
conform to the Intel386 ABI. Programs built with the doubleword alignment
facility can thus violate conformance to the Intel386 ABI. See ‘‘Aggregates
and Unions’’ below and ‘‘Function Calling Sequence’’ later in this chapter
for more information.
GCC 不想违反结构的 ABI(其中对齐非常相关),因此它正确地将 4 的对齐用于结构内部的双精度值。
This behavior is even contrary to the ISO C standard
ISO C 在这里完全不相关,因为 __alignof__
不是任何 C 标准的一部分。编译器可以做任何事情,比如从网上抓一张猫的照片给你看,那是完全符合C标准的行为。
C11 确实指定了 _Alignof
运算符。有趣的是,如果我们使用作为 C11 标准一部分的 _Alignof
运算符,GCC 会报告其他(正确的)数字:
$ 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
C 标准的措辞并没有具体说明在 ABI 中应该发生什么,在 ABI 中,结构内部的类型比外部的类型对齐度更低。
After careful reading of the standard's wording, and some debate, gcc developers decided that _Alignof
should tell you the minimum alignment that you will ever see for a value of that type in a strict C11 program (https://gcc.gnu.org/ml/gcc-patches/2013-12/msg00435.html)。 (这就是您想要的用例,例如编写一个扫描内存块以查找潜在指针的垃圾收集器。)请注意,C11 不包括 __attribute__((packed))
,并且强制转换未对齐的指针是 UB。
This mailing list post 解释了为什么他们更改了 C11 _Alignof
,而不是 C++ alignof
或 GNU __alignof__
扩展。
GNU C 的 __alignof__
继续表示 gcc 将如何在结构之外将该类型对齐为全局或局部类型。即 maximum/recommended 对齐。 i386 SysV ABI 的当前版本并没有说明任何有关将 double
对齐到 8B 的内容;这纯粹是当前编译器为提高性能而采取的可选行为。
_Alignof(double) <= _Align(struct containing_double)
似乎满足 C11 标准中的所有要求,即使 double
的首选对齐方式是 8B。 double
在跨越 4B 边界时有效,如果它跨越缓存行或页面则速度较慢。
(但请注意,如果 _Atomic long long
不是 8B 对齐的,它就不会“工作”,因此 clang 甚至在结构内部也给它 8B 对齐。Current gcc is broken 对于 32- 上的 C11 stdatomic 8B 类型位 SysV ABI,并有望更改以匹配 clang。)
在clang里,_Alignof
好像和__alignof__
一样。所以它不同意 gcc 关于 C11 运算符(但不关于结构布局,除了 C11 stdatomic)。
查看一些使用 gcc7.2 和 clang4.0 的测试用例 on the Godbolt compiler explorer。删除 -xc
以编译为 C++ 而不是 C
有些相关:gcc7 将 32 位中 max_align_t
的对齐从 8 增加到 16,for _Float128
,但 malloc(8)
或 strdup("abc")
可能仍然 return 只有 8B 对齐的块。
gcc's stddef.h
implements max_align_t
结构的成员如
long long __max_align_ll __attribute__((__aligned__(__alignof__(long long))));
确保生成的结构确实具有与其成员一样大的对齐要求 (_Alignas
)。它还有 long double
和 __float128
成员。
我在 x86_64 机器上,Ubuntu 16.04 和 gcc 5.4.0。
我今天遇到了这种有点奇怪的行为。
$ 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 个 double
和 4 个 struct dd
。我认为 struct
以比其成员更小的单位对齐很奇怪。这种行为甚至违背了 gcc 5.4.0 doc:
Note that the alignment of any given
struct
orunion
type is required by the ISO C standard to be at least a perfect multiple of the lowest common multiple of the alignments of all of the members of thestruct
orunion
in question.
造成这种行为的原因可能是什么?将此 struct
对齐到 4 字节是访问内存的优化方式吗?
指定它在您的体系结构上的行为方式的文档是 here, in the i386 System V psABI. (Current revision here, see also the x86 标签 wiki)。在其中我们可以读到 double 所需的对齐方式是 4。但是它有这个有趣的注释:
The Intel386 architecture does not require doubleword alignment for double-precision values. Nevertheless, for data structure compatibility with other Intel architectures, compilers may provide a method to align double-precision values on doubleword boundaries.
A compiler that provides the doubleword alignment mentioned above can generate code (data structures and function calling sequences) that do not conform to the Intel386 ABI. Programs built with the doubleword alignment facility can thus violate conformance to the Intel386 ABI. See ‘‘Aggregates and Unions’’ below and ‘‘Function Calling Sequence’’ later in this chapter for more information.
GCC 不想违反结构的 ABI(其中对齐非常相关),因此它正确地将 4 的对齐用于结构内部的双精度值。
This behavior is even contrary to the ISO C standard
ISO C 在这里完全不相关,因为 __alignof__
不是任何 C 标准的一部分。编译器可以做任何事情,比如从网上抓一张猫的照片给你看,那是完全符合C标准的行为。
C11 确实指定了 _Alignof
运算符。有趣的是,如果我们使用作为 C11 标准一部分的 _Alignof
运算符,GCC 会报告其他(正确的)数字:
$ 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
C 标准的措辞并没有具体说明在 ABI 中应该发生什么,在 ABI 中,结构内部的类型比外部的类型对齐度更低。
After careful reading of the standard's wording, and some debate, gcc developers decided that _Alignof
should tell you the minimum alignment that you will ever see for a value of that type in a strict C11 program (https://gcc.gnu.org/ml/gcc-patches/2013-12/msg00435.html)。 (这就是您想要的用例,例如编写一个扫描内存块以查找潜在指针的垃圾收集器。)请注意,C11 不包括 __attribute__((packed))
,并且强制转换未对齐的指针是 UB。
This mailing list post 解释了为什么他们更改了 C11 _Alignof
,而不是 C++ alignof
或 GNU __alignof__
扩展。
GNU C 的 __alignof__
继续表示 gcc 将如何在结构之外将该类型对齐为全局或局部类型。即 maximum/recommended 对齐。 i386 SysV ABI 的当前版本并没有说明任何有关将 double
对齐到 8B 的内容;这纯粹是当前编译器为提高性能而采取的可选行为。
_Alignof(double) <= _Align(struct containing_double)
似乎满足 C11 标准中的所有要求,即使 double
的首选对齐方式是 8B。 double
在跨越 4B 边界时有效,如果它跨越缓存行或页面则速度较慢。
(但请注意,如果 _Atomic long long
不是 8B 对齐的,它就不会“工作”,因此 clang 甚至在结构内部也给它 8B 对齐。Current gcc is broken 对于 32- 上的 C11 stdatomic 8B 类型位 SysV ABI,并有望更改以匹配 clang。)
在clang里,_Alignof
好像和__alignof__
一样。所以它不同意 gcc 关于 C11 运算符(但不关于结构布局,除了 C11 stdatomic)。
查看一些使用 gcc7.2 和 clang4.0 的测试用例 on the Godbolt compiler explorer。删除 -xc
以编译为 C++ 而不是 C
有些相关:gcc7 将 32 位中 max_align_t
的对齐从 8 增加到 16,for _Float128
,但 malloc(8)
或 strdup("abc")
可能仍然 return 只有 8B 对齐的块。
gcc's stddef.h
implements max_align_t
结构的成员如
long long __max_align_ll __attribute__((__aligned__(__alignof__(long long))));
确保生成的结构确实具有与其成员一样大的对齐要求 (_Alignas
)。它还有 long double
和 __float128
成员。