Я хотел бы иметь точный контроль над тем, как автоматические переменные освобождаются из стека.
Здесь много путаницы.Оптимизирующий компилятор 1006 * может хранить некоторые автоматические переменные только в регистрах , без использования какого-либо слота в кадре вызова.Спецификация языка C ( n1570 ) не требует никакого стека вызовов.
И данный регистр или слот в кадре вызова можно повторно использовать для различных целей (например, для различных автоматических переменных вразличные части функции). Распределение регистров играет важную роль компиляторов.
Могу ли я всегда ожидать, что в точке измерения стек будет содержать только буфер2 и буфер1 освобожден?
Конечно, нет.Компилятор может доказать, что на более позднем этапе в вашем коде пространство для buffer1
больше не будет полезным, поэтому повторно используйте это пространство для других целей.
, есть ли способ, которым я могу иметь точный контроль надосвобождение стека?
Нет , нет. стек вызовов является подробностью реализации и может не использоваться (или «злоупотребляться» с вашей точки зрения) компилятором и сгенерированным кодом.
В некоторых глупых примерах:если buffer1
не используется в foo
, компилятор может не выделить для него место.И некоторые умные компиляторы могут просто выделить 8 байтов, если они могут доказать, что только 8 первых байтов buffer1
полезны.
Если серьезно, в некоторых случаях GCC может tail-call оптимизаций.
Вас должно заинтересовать , вызывающее GCC с -fstack-reuse=all
, -Os
, -Wstack-usage=256
, -fstack-usage
идругие варианты.
Конечно, использование конкретного стека зависит от уровней оптимизации.Вы также можете проверить сгенерированный ассемблерный код, например, с помощью -S -O2 -fverbose-asm
Например, следующий код e.c
:
int f(int x, int y) {
int t[100];
t[0] = x;
t[1] = y;
return t[0]+t[1];
}
при компиляции с GCC8.1 в Linux / Debian /x86-64 с использованием gcc -S -fverbose-asm -O2 e.c
дает e.s
.text
.p2align 4,,15
.globl f
.type f, @function
f:
.LFB0:
.cfi_startproc
# e.c:5: return t[0]+t[1];
leal (%rdi,%rsi), %eax #, tmp90
# e.c:6: }
ret
.cfi_endproc
.LFE0:
.size f, .-f
, и вы видите, что кадр стека не увеличен на 100 * 4 байта.И это все еще имеет место с:
int f(int x, int y, int n) {
int t[n];
t[0] = x;
t[1] = y;
return t[0]+t[1];
}
, который фактически генерирует тот же машинный код, что и выше.И если вместо +
выше я вызываю inline int add(int u, int v) { return u+v; }
, то сгенерированный код не меняется.
Помните о как-бы правиле и о сложном понятии неопределенное поведение (если n
было 1 выше, это UB).