В чем разница между обычной функцией и статической функцией "только с точки зрения выделения памяти"?
Ничего.Статическая функция похожа на глобальную функцию, за исключением области действия.
Даже для нестатических функций-членов дополнительная память не требуется.Функция-член int C::f(int arg1, int arg2)
является просто синтаксическим сахаром для чего-то вроде int C__f(C* this, int arg1, int arg2)
.
Что если функция-член содержит некоторые локальные переменные?В этом случае функция «должна» иметь отдельную копию этой переменной - специфическую для объекта, вызывающего функцию ...
Существует копия локальных переменных для каждого вызова функции (если они static
).Вот почему возможна рекурсия в C ++.
Как эта проблема решается в C ++?
Вызовы функций основаны на "кадрах стека".Кадр стека состоит из:
- Аргументы функции (включая неявные
this
, если применимо). - Любые другие не
static
локальные переменные в функции. - «Адрес возврата», который сообщает процессору, где возобновить выполнение после выполнения функции.
При каждом вызове функции создается кадр стека.Когда функция включена, кадр стека уничтожается.Если функция вызывается рекурсивно, каждый уровень рекурсии получает свой собственный кадр стека.Например, если у вас есть
int factorial(int n) {
if (n <= 1)
return 1;
else
return factorial(n - 1) * n;
}
Затем, когда вы вызываете factorial(3)
, кадр стека создается следующим образом:
------------------------ stack pointer (SP)
n = 3
RA = <in main()>
Когда выполняется рекурсивный вызов factorial(2)
дополнительный кадр добавляется к вершине стека
------------------------ SP
n = 2
RA = <in factorial()>
------------------------
n = 3
RA = <in main()>
Еще один рекурсивный вызов выполняется для factorial(1)
.
------------------------ SP
n = 1
RA = <in factorial()>
------------------------
n = 2
RA = <in factorial()>
------------------------
n = 3
RA = <in main()>
Это базовый случай для рекурсии,и возвращаемое значение 1 сохраняется в регистре.После завершения вызова функции верхний кадр стека уничтожается, и выполнение продолжается по сохраненному адресу возврата.
------------------------ SP
n = 2
RA = <in factorial()>
------------------------
n = 3
RA = <in main()>
Теперь при вызове factorial(2)
можно вычислить его возвращаемое значение (2) и другой стеккадр может быть уничтожен:
------------------------ SP
n = 3
RA = <in main()>
Наконец, мы можем вычислить результат исходного вызова функции (6) и также уничтожить этот стек стека.