将堆栈视为函数堆栈帧的序列,而不是变量地址的序列。无论堆栈向哪个方向增长,它都会以整个堆栈帧的增量增长,每个函数的堆栈帧大小不同。
函数堆栈帧的布局对于绑定变量有固定的位置,类似于结构体,但不能保证帧内绑定的确切顺序。如果可以通过不同的布局使该功能更有效地利用空间,那么它可能会实现。例如:
fn main() {
let i1: i32 = 1;
let i2: i64 = 2;
let i3: i32 = 3;
println!("i1 : {:?}", &i1 as *const i32);
println!("i2 : {:?}", &i2 as *const i64);
println!("i3 : {:?}", &i3 as *const i32);
}
// i1 : 0x7fff4b9271fc
// i2 : 0x7fff4b927200
// i3 : 0x7fff4b92720c
Here, i3
被储存了before i2
. An i64
需要与 64 位的倍数对齐,因此存储这两个值更加紧凑i32
在一起而不是留下间隙。这在调试版本和编译器中不会发生could也选择了存储i3
首先具有相同的效果,因此我们不能也不应该依赖这种顺序。
出于任何其他优化原因(例如缓存访问效率),变量也可能被重新排序。
要查看堆栈实际上是向下增长的,请考虑一个具有多个函数的示例:
fn main() {
let i1 = 1;
println!("i1 : {:?}", &i1 as *const i32);
another();
}
#[inline(never)]
fn another() {
let i2 = 2;
println!("i2 : {:?}", &i2 as *const i32);
}
// i1 : 0x7fffc7601fbc
// i2 : 0x7fffc7601f5c
another
被称为main
所以它的堆栈帧有一个较低的地址。请注意,我必须强制编译器不要内联该函数,否则组合布局将是任意的。