Почему вызов функции, а не переменные адреса, используется для определения направления роста стека? - PullRequest
8 голосов
/ 21 мая 2011

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

Однако в этом классическом вопросе к собеседованию я хочу понять , почему люди используют вызов функции, а не сравнивают 2 локальные переменные в одной и той же функции. Я думаю, что для этого должна быть какая-то особая причина, но, не будучи разработчиком на низком уровне C / Java [Java :)], я просто догадываюсь.

Вот код, который я пробовал:

void sub (int *a)  {
    int b;
    int c;
    printf ("a:%d\n", a);
    printf ("b:%d\n", &b);
    printf ("c:%d\n", &c);
    if (&b > a) {
        printf ("Stack grows up.\n");
    } else {
        printf ("Stack grows down.\n");
    }
}

int main (void) {
    int a;
    int b;
    sub (&a);
    printf ("\nHere we go again!!\n");
    if (&b > &a)  {
        printf ("Stack grows up.\n");
    } else  {
        printf ("Stack grows down.\n");
    }
    return 0;
}

Я также нашел эту статью, которая пытается оптимизировать решение, которое я тоже не понимаю: http://www.devx.com/tips/Tip/37412

P.S .: Из разных ответов на эту и другие темы кажется, что сам вопрос ошибочен / неактуален, поскольку в качестве вопроса об интервью он, вероятно, усиливает неверные предположения, если кто-то не исследует ответ!

Спасибо!

Ответы [ 5 ]

6 голосов
/ 21 мая 2011

Вы не можете полностью контролировать порядок, в котором компилятор выбирает размещение локальных переменных. Однако вы можете разумно контролировать функции, которые будут вызываться, и в каком порядке.

5 голосов
/ 21 мая 2011

Порядок, в котором объявленные переменные будут помещены в стек, не определен, но в функции, вызываемой функцией, аргументы внутреннего вызова функции обязательно будут помещены в стек позже, чем внешние функции.

4 голосов
/ 21 мая 2011

Компиляторы могут и изменять порядок переменных в кадрах стека:

#include <stdio.h>

int main ()
{
    char c1;
    int a;
    char c2;
    int b;

    printf("%p %p %p %p\n", &c1, &a, &c2, &b);
    return 0;
}

print

0x7ffff62acc1f 0x7ffff62acc18 0x7ffff62acc1e 0x7ffff62acc14

здесь (с использованием gcc 4.4.3 в 64-битном Linux).c2 был перемещен рядом с c1.

4 голосов
/ 21 мая 2011

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

int i;
double j;

может иметь i до или после j. Пока компиляция генерирует правильный код для доступа к переменной, она может идти куда угодно.

Фактически, если вы не используете оператор адреса & (или иначе не должны получить адрес), переменная никогда не может даже быть в стеке. Может храниться в регистре на время разговора.

Однако порядок размещения самих стековых фреймов ограничен, поскольку, если они вышли из строя, функция возврата не будет работать так хорошо (мягко говоря).


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

3 голосов
/ 21 мая 2011

Проблема в том, что когда вы делаете это:

void test(void)
{
    int a;
    int b;
    if (&a < &b)
        ...

Получаемый результат не имеет ничего общего с направлением роста стека.Единственный способ узнать, как растет стек, - это создать новые кадры .Компилятор может свободно ставить a выше b или a ниже b по своему усмотрению.Разные компиляторы могут давать разные результаты.

Однако, если вы вызываете другую функцию, локальные переменные этой функции имеют в новом кадре стека, т. Е. В направлении роста от вызывающей стороны.переменные.

...