То, как меня учили довольно долго, заключается в том, что когда я запускаю программу, первое, что сразу идет в стек, - это стековый фрейм для основного метода. И если я вызываю функцию foo() из main, тогда стек стека, размер локальных переменных (автоматические объекты) и параметры также попадают в стек.Действительно ли стек стека попадает в стек при вызове функции?
Однако я столкнулся с несколькими вещами, которые противоречат этому. И я надеюсь, что кто-то сможет очистить мое замешательство или объяснить, почему на самом деле нет никаких противоречий.
Первое противоречие:
В книге «Язык программирования C++» третье издание Бьярне Страуструп, он говорит на странице 244, «Именованная автоматический объект создается каждый раз, когда его декларация встречается в исполнении программа." Если это недостаточно ясно, на следующей странице говорится: «Конструктор локальной переменной выполняется каждый раз, когда поток управления проходит через объявление локальной переменной».
Означает ли это, что общая память для фрейма стека не распределяется сразу, а скорее блокируется по мере того, как встречаются объявления переменных? Кроме того, означает ли это, что кадр стека может не быть одного и того же размера каждый раз, если объявление переменной не встречалось из-за оператора if?
Второе противоречие:
Я сделал немного кодирования в сборке (ARM, чтобы быть конкретным), и как учили мой класс был, что когда функция была вызвана, мы сразу не использовали регистры и никогда не толкнули любая из локальных переменных текущей функции в стек, если алгоритм не может выполняться с ограниченным количеством регистров. И даже тогда мы только толкали оставшиеся переменные.
Означает ли это, что когда функция вызывается, фрейм стека вообще не может быть создан? Означает ли это, что кадр стека может отличаться по размеру из-за использования регистров?
Возможно, вы захотите посмотреть [Ручной рычаг и указатель кадра] (http://stackoverflow.com/questions/15752188/arm-link-register-and-frame-pointer), в основном * Мы хотим, чтобы компиляторы были такими же быстро можно *, поэтому нет общего правила о том, как они должны генерировать код ассемблера, особенно при оптимизации. Если компилятор может * вывести * вещи, как в функции листа, он воспользуется этим фактом. Компилятор должен создать пространство для объекта, если он не является ** POD **, и он не знает о реализации конструктора; он может сразу же выделить или нет, если он хочет уменьшить использование стека, выбрать кеш и т. д. –