путаница в стеке вызовов функций - PullRequest
10 голосов
/ 21 сентября 2011

Согласно Wiki:

вызывающая сторона помещает адрес возврата в стек, а вызываемый подпрограмма, когда она заканчивается, выскакивает обратный адрес из вызова стек и передает управление по этому адресу.

Картинка из Вики:

enter image description here

Я не совсем понимаю это. Скажем, у меня есть программа на C следующим образом:

#include <stdio.h>

int foo(int x)
{
    return x+1;
}

void spam()
{
    int a = 1;  //local variable
    int b = foo(a);  //subroutine called
    int c = b;  //local variable
}

int main()
{
    spam();
    return 0;
}

И я думаю, что стек вызовов должен выглядеть примерно так:

<None> means none local variables or params

      _| parameters for foo() <int x>  |_
top    | local of spam() <int c>       |
^      | return address of foo()       |<---foo() called, when finishes, return here?
|      | local of spam() <int b>       |
bot    | local of spam() <int a>       |
      _| parameters for spam() <None>  |_
       | locals of main() <None>       | 
       | return address of spam()      |<---spam() called, when finishes, return here?
       | parameters for main() <None>  |

Вопрос:

Согласно словам, цитируемым из вики,

вызываемая подпрограмма, когда она заканчивается, выскакивает адрес возврата стек вызовов и передает управление по этому адресу.

1. Мой рисунок прав?

2. Если это правильно, то когда foo () завершится, он будет

вытолкнуть адрес возврата из стека вызовов и передать управление этот адрес

, но как оно может всплыть с обратного адреса? Потому что, когда foo заканчивается, текущий указатель стека указывает на локальный спам, право?

UPDATE:

что если main () выглядит так:

int main()
{ 
    spam();
    foo();
}

тогда как должен выглядеть стек вызовов?

1 Ответ

15 голосов
/ 21 сентября 2011

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

Должно быть так:

| parameters for foo() <int x>  |
| return address of foo()       |
| local of spam() <int c>       |
| local of spam() <int b>       |
| local of spam() <int a>       |
| parameters for spam() <None>  |
| return address of spam()      |
| locals of main() <None>       | 
| parameters for main() <None>  |

Я думаю, что путаница заключается в том, что вы считаете, что объявления переменных рассматриваются как операторы и выполняются по порядку. Фактически, компилятор обычно анализирует функцию, чтобы решить, сколько стекового пространства необходимо для всех локальных переменных. Затем он генерирует код для соответствующей настройки указателя стека, и эта настройка выполняется при входе в функцию. Любые вызовы других функций могут затем помещаться в стек без вмешательства в кадр стека этой функции.

...