Будем рассматривать каждый ВЫЗОВ функции как помещение в специальный стек большого "блока информации", включающего в частности АРГУМЕНТЫ И ЛОКАЛЬНЫЕ ПЕРЕМЕННЫЕ вызываемой функции.
Оператор return из вызванной функции выталкивает со стека ВЕСЬ такой блок.
В качестве примера рассмотрим такую программу:
int x = 7; /* глобальная */ int v = 333; /* глобальная */
int factorial(int n){ int w; /* лишняя переменная, только для демонстрационных целей */
w = n;
if(n == 1) return 1; /* #a */ else return n * factorial(n-1); /* #b */ } void func(){ int x; /* func::x */
x = 777; /* #c */ printf("Вызвана функция func()\n"); } void main(){ int y = 12; /* main::y */ int z;
/* A */ z = factorial(3); /* B */ printf("z=%d\n", z);
func(); /* C */ }
Выполнение программы начнется с вызова функции main(). В точке /* A */
| в з г л я д | V V
--------+ +-------- |=======================| | z = мусор | | y = 12 | +------+---------+ |main() | |x = 7 | v = 333 | +-----------------------+-----------+------+---------+----- СТЕК ВЫЗОВОВ ФУНКЦИЙ ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ
В каждый данный момент видимы переменные, которые находятся a) на вершине (и только) стека вызовов функций. b) и незаслоненные ими глобальные переменные. В данном случае мы смотрим "сверху" и видим:
main::z, main::y, ::x, ::v
В точке /* B */ мы вызываем factorial(3).
--------+ +-------- |=======================| | w = мусор | | n = 3 | |factorial(3) | |=======================| | +-|---> текущий оператор z = factorial(3); | z = мусор | | y = 12 | +------+---------+ |main() | |x = 7 | v = 333 | +-----------------------+-----------+------+---------+----- СТЕК ВЫЗОВОВ ФУНКЦИЙ ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ
При новом взгляде видимы: factorial(3)::w, factorial(3)::n, ::x, ::v
Стали невидимы: main::z, main::y
Строка "текущий оператор ..." указывает место, с которого надо возобновить выполнение функции, когда мы вернемся в нее.
Когда выполнение программы в функции factorial(3) дойдет до точки /* #b */ будет выполняться return 3 * factorial(2). В итоге будет вызвана функция factorial(2).