简体   繁体   中英

Why do subsequent Rust variables increment the stack pointer instead of decrementing it?

I find it odd how when you create statically-allocated variables in Rust that it seems as the stack pointer increases. I know this is not the case since the stack pointer decreases as memory is allocated.

If I were to do the same thing in C, I would see the stack pointer decrease as I created more variables.

Why is it this way? Does the Rust compiler allocate these from bottom to top instead on top to bottom?

fn main() {
    let i1 = 1;
    let i2 = 1;
    let i3 = 1;
    println!("i1 : {:?}", &i1 as *const i32);
    println!("i2 : {:?}", &i2 as *const i32);
    println!("i3 : {:?}", &i3 as *const i32);
}

When i run this program, I receive this:

i1 : 0x9f4f99fb24
i2 : 0x9f4f99fb28
i3 : 0x9f4f99fb2c

If I used C, I would get this:

i1 : 0x9f4f99fb2c
i2 : 0x9f4f99fb28
i3 : 0x9f4f99fb24

Think of the stack as a sequence of function stack frames, not a sequence of variable addresses. Regardless of the direction that the stack grows, it grows in increments of whole stack frames, which are different sizes for each function.

The layout of a function's stack frame has fixed positions for where variables will be bound, similar to a struct, but the exact order of bindings within the frame is not guaranteed. If the function can be made to use space more efficiently with a different layout, it probably will. For example:

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 is stored before i2 . An i64 needs to be aligned to a multiple of 64 bits so it is more compact to store the two i32 s together rather than leaving a gap. This doesn't happen in debug builds, and the compiler could also have chosen to store i3 first with the same effect, so we cannot and should not rely on this ordering.

It's also possible that variables could be reordered for any other optimisation reasons, such as cache access efficiency.


To see that the stack does actually grow downwards, consider an example with multiple functions:

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 is called by main so its stack frame has a lower address. Notice that I had to force the compiler not to inline the function, otherwise the combined layout would have been arbitrary.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM