针对 x86 架构使用 gcc 编译器的双堆栈对齐问题
Double stack alignment question using gcc compiler for x86 architecture
关于数据类型对齐的疑问,我现在正在学习对齐,我有一些问题,所以我知道在linux中用gcc编译i386架构时双对齐到4字节,所以地址double 的 has 对齐到 4 的倍数,但是当我使用堆栈时它不会发生,只有当我使用数据结构时才会发生
#include <stdio.h>
int main(void) {
double x = 5; // 8
char s = 'a'; // +1
double y = 2; // ---- = 9 + 8 = 17 + alignment = 20
//int x = 5; // 4
//char s = 'a'; +1
//int y = 2; --------= 5 + 4 = 9 + alignment = 12
size_t a, b;
a = (size_t)&s;
b = (size_t)&y;
printf("%zu", a - b); // it wasn't supposed to be 11 instead of 15
return 0;
}
编译:
$ gcc -m32 -o 对齐 align.c
出于优化的原因,编译器可以而且确实会选择为对象提供更多对齐方式,除非 ABI 的结构打包规则强制它们不对齐。 alignof(double) = 4
对于 i386 System V,但 gcc 更喜欢 以使其自然对齐,例如 aligas(sizeof(double)) double x
.
现代 x86 受益于 double
的 8 字节对齐,但 alignof(double) == 4
ABI 规则在 386 天之前就有意义了t 缓存,并且 fld qword [mem]
确实需要 2 个独立的 32 位加载。但是现代 x86 硬件可以在对缓存的单次访问中执行 8 字节(甚至 32 字节)加载,if 它不会拆分为两个缓存行,这可以通过 alignof < sizeof.
此外,将堆栈按 16 位对齐使得为本地人提供 8 或 16 字节对齐变得便宜是对 i386 System V ABI 的更新 Linux 修改。在此之前,每个具有 double
本地的函数都将被迫创建一个帧指针并执行 and $-8, %esp
或其他操作。
不知道为什么你认为编译器会按照声明的顺序排列局部变量,就像它是一个结构一样。编译器可以自由地将大对象放在一起,因此不必在填充上浪费 space。
查看编译器 asm 输出以了解其堆栈布局发生了什么。并且不要忘记启用优化。您可以使用 volatile double
来阻止其内存地址优化。
关于数据类型对齐的疑问,我现在正在学习对齐,我有一些问题,所以我知道在linux中用gcc编译i386架构时双对齐到4字节,所以地址double 的 has 对齐到 4 的倍数,但是当我使用堆栈时它不会发生,只有当我使用数据结构时才会发生
#include <stdio.h>
int main(void) {
double x = 5; // 8
char s = 'a'; // +1
double y = 2; // ---- = 9 + 8 = 17 + alignment = 20
//int x = 5; // 4
//char s = 'a'; +1
//int y = 2; --------= 5 + 4 = 9 + alignment = 12
size_t a, b;
a = (size_t)&s;
b = (size_t)&y;
printf("%zu", a - b); // it wasn't supposed to be 11 instead of 15
return 0;
}
编译: $ gcc -m32 -o 对齐 align.c
出于优化的原因,编译器可以而且确实会选择为对象提供更多对齐方式,除非 ABI 的结构打包规则强制它们不对齐。 alignof(double) = 4
对于 i386 System V,但 gcc 更喜欢 以使其自然对齐,例如 aligas(sizeof(double)) double x
.
现代 x86 受益于 double
的 8 字节对齐,但 alignof(double) == 4
ABI 规则在 386 天之前就有意义了t 缓存,并且 fld qword [mem]
确实需要 2 个独立的 32 位加载。但是现代 x86 硬件可以在对缓存的单次访问中执行 8 字节(甚至 32 字节)加载,if 它不会拆分为两个缓存行,这可以通过 alignof < sizeof.
此外,将堆栈按 16 位对齐使得为本地人提供 8 或 16 字节对齐变得便宜是对 i386 System V ABI 的更新 Linux 修改。在此之前,每个具有 double
本地的函数都将被迫创建一个帧指针并执行 and $-8, %esp
或其他操作。
不知道为什么你认为编译器会按照声明的顺序排列局部变量,就像它是一个结构一样。编译器可以自由地将大对象放在一起,因此不必在填充上浪费 space。
查看编译器 asm 输出以了解其堆栈布局发生了什么。并且不要忘记启用优化。您可以使用 volatile double
来阻止其内存地址优化。