Обратите внимание на то, что функция term
d
, которую необходимо хранить в памяти, поэтому при вызове мы резервируем для нее место, даже если в коде она нигде не используется. Все в порядке. Для корректных функций (а именно такие функции мы явно или неявно используем в нашей книге) затраты на создание активизационных записей не зависят от их размера. Локальная переменная d
будет инициализирована только в том случае, если будет выполнен раздел case '/'
.Теперь функция term
primary
, и мы получаем следующую картину.Все это становится довольно скучным, но теперь функция primary
expression
. expression
expression
. Хорошо это или плохо, но мы теперь попадаем в очень запутанную ситуацию, поскольку переменные left
и t
при двух разных вызовах будут разными. Функция, которая прямо или (как в данном случае) косвенно вызывает себя, называется Итак, каждый раз, когда мы вызываем функцию
expression
управление возвращается функции primary
, стек возвращается в предыдущее состояние.Когда функция primary
term
, стек возвращается в состояние, показанное ниже.И так далее. Этот стек, который часто называют
Запомните, что детали реализации стека зависят от реализации языка С++, но в принципе соответствуют схеме, описанной выше. Надо ли вам знать, как реализованы вызовы функции? Разумеется, нет; мы и до этого прекрасно обходились, но многие программисты любят использовать термины “активационная запись” и “стек вызовов”, поэтому лучше понимать, о чем они говорят.
8.6. Порядок вычислений
Выполнение программы происходит инструкция за инструкцией в соответствии с правилами языка. Когда поток выполнения достигает определения переменной, происходит ее создание, т.е. в памяти выделяется память для объекта, и этот объект инициализируется. Когда переменная выходит из области видимости, она уничтожается, т.е. объект, на который она ссылалась, удаляется из памяти, и компилятор может использовать ранее занимаемый им участок памяти для других целей. Рассмотрим пример.
string program_name = "silly";
vector
void f
{
string s; // s — локальная переменная в функции f
while (cin>>s && s!="quit") {
string stripped; // stripped — локальная переменная в цикле
string not_letters;
for (int i=0; i
// видимости инструкции
if (isalpha(s[i]))
stripped += s[i];
else
not_letters += s[i];
v.push_back(stripped);
// ...
}
// ...
}
Глобальные переменные, такие как program_name
v
, инициализируются до выполнения первой инструкции функции main
. Они существуют, пока программа не закончит работу, а потом уничтожаются. Они создаются в порядке следования своих определений (т.е. переменная program_name создается до переменной v
), а уничтожаются — в обратном порядке (т.е. переменная v
уничтожается до переменной program_name
).Когда какая-нибудь функция вызывает функцию f
s;
иначе говоря, переменная s
инициализируется пустой строкой. Она будет существовать, пока функция f
не вернет управление. Каждый раз, когда мы входим в тело цикла while
, создаются переменные stripped
и not_letters
. Поскольку переменная stripped
определена до переменной not_letters
, сначала создается переменная stripped
. Они существуют до выхода из тела цикла. В этот момент они уничтожаются в обратном порядке (иначе говоря, переменная not_letters
уничтожается до переменной stripped
) и до того, как произойдет проверка условия выхода из цикла. Итак, если, до того, как мы обнаружим строку quit
, мы выполним цикл десять раз, переменные stripped
и not_letters
будут созданы и уничтожены десять раз.