Почему последующие переменные Rust увеличивают указатель стека, а не уменьшают его? - PullRequest
0 голосов
/ 28 января 2019

Я нахожу странным, что когда вы создаете статически размещенные переменные в Rust, это выглядит как увеличение указателя стека.Я знаю, что это не тот случай, так как указатель стека уменьшается с выделением памяти.

Если бы я делал то же самое в C, я бы увидел уменьшение указателя стека при создании большего количества переменных.

Почему так?Распределяет ли их компилятор Rust снизу вверх вместо сверху вниз?

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);
}

Когда я запускаю эту программу, я получаю это:

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

Если бы я использовал C, я бы получил это:

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

1 Ответ

0 голосов
/ 28 января 2019

Думайте о стеке как о последовательности кадров стека функций, а не о последовательности адресов переменных.Независимо от направления, в котором растет стек, он увеличивается с приращением целых кадров стека, которые имеют разные размеры для каждой функции.

Макет кадра стека функции имеет фиксированные позиции для привязки переменных, аналогичнов структуру, но точный порядок привязок в кадре не гарантируется.Если функция может быть использована для более эффективного использования пространства с другим макетом, это, вероятно, будет.Например:

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

Здесь i3 сохраняется до i2.i64 должен быть выровнен по кратному 64 битам, поэтому более компактно хранить два i32 вместе, а не оставлять пробел.Этого не происходит в отладочных сборках, и компилятор мог бы также предпочесть сначала сохранить 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, поэтому его стековый фрейм имеет меньший адрес.Обратите внимание, что мне пришлось заставить компилятор не вставлять функцию в строку, иначе комбинированный макет был бы произвольным.

...