Мне трудно понять запись активации (я прочитал несколько ответов об этом).
Предположим, у нас есть код
int n( int a){
int b = a/2;
return a + b;
}
int main (){
int first = 1;
int second = n(first);
int third = 3;
int fourth = n(third);
return 0;
}
когда программа начнет выполняться, стек будет заполнен, например,
| first |
__________| activation_record |
| first |
____________________| third |
| activation_record |
| first |
_____________________| activation_record1 |
| third |
| activation_record |
| first |
_______________________
запись активации помещает статические локальные переменные, адрес функции, параметры функции и возвращаемое значение в свой стек, если я понимаю это так, что после выполнения записи активации (или функции вызываемого) запись активации заменяется ее возвращаемым значением. и его стек освобожден?
Также при одной и той же функции, вызываемой несколько раз, и наличии стека вызовов, который должен содержать место для возврата данных, будет ли та же самая Activation_record помещена в стек, или она создается каждый раз, когда вызывается функция? При всем остальном можно ли поместить запись активации в стек во время компиляции?
Спасибо за ответ
Вы получаете разные ответы, потому что возможны разные решения. Стандарт C ++ описывает только наблюдаемое поведение, а макеты стека не наблюдаемы.
В частности, современные компиляторы уменьшат вашу программу до int main() { return 0; }
потому что только возвращаемое значение является наблюдаемым.
Если бы вы написали return fourth;
современные компиляторы поняли бы, что fourth==4
и заменил программу на return 4
,
Но давайте на минутку предположим, что этих оптимизаций не происходит, и у вас есть обычный x64-компилятор. Опять же, результаты будут другими: обычный ABI x64 передает аргументы функции и возвращает значения в регистрах ЦП, а не в стеке. Это не использует все регистры x64, поэтому локальные переменные могут также зайти в реестр.
Различные записи активации также будут перекрываться, так как они не используются одновременно. На самом деле это не оптимизация, а необходимость, потому что компилятор в целом не может определить, сколько вызовов будет сделано. Пример: for (char&c : string) { c = toupper(c); }
,
Других решений пока нет …