Некоторые из них могут быть пространством для исходящих параметров, если ваша функция вызывает другие; часть из них может быть временным пространством для значений, вылитых из регистров; некоторые из них могут быть дополнением. Это будет очень зависеть от версии компилятора и флагов оптимизации.
Вот несколько простых бессмысленных кодов для иллюстрации:
extern int foo(int a, int b);
int bar(int c)
{
char s[12];
s[0] = foo(c, 123);
return 456;
}
Здесь он скомпилирован без оптимизации с использованием gcc 4.3.2 на компьютере Debian Lenny:
bar:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $123, 4(%esp)
movl 8(%ebp), %eax
movl %eax, (%esp)
call foo
movb %al, -12(%ebp)
movl $456, %eax
leave
ret
Как и ваш код, он выделяет 24 байта. Вот для чего они используются:
Stack while running bar()
: :
+-------------------------+
| incoming parameter: c | 8(%ebp)
+-------------------------+ ---
| return address | 4(%ebp) ^
+-------------------------+ |
| old %ebp | (%ebp) |
+-------------------------+ | bar()'s stack
| s[8]..s[11] | -4(%ebp) | frame: 32 bytes
+-------------------------+ |
| s[4]..s[7] | -8(%ebp) |
+-------------------------+ |
| s[0]..s[3] | -12(%ebp) |
+-------------------------+ | Stack while running foo()
| (unused) | 8(%esp) | : :
+-------------------------+ | +-------------------------+
| outgoing parameter: 123 | 4(%esp) | | incoming parameter: b |
+-------------------------+ | +-------------------------+
| outgoing parameter: c | (%esp) v | incoming parameter: a |
+-------------------------+ --- +-------------------------+
| return address |
+-------------------------+
| old %ebp |
+-------------------------+
: locals for foo() :
Немного эксперимента покажет, что если s[]
увеличится, он попадет в неиспользуемое пространство; например если это 13 байтов, кадр стека имеет тот же размер, но s[]
начнется на один байт раньше (в -13(%ebp)
) - до 16 байтов, где фактически будет использоваться весь выделенный стек. Если s
объявлено как s[17]
, компилятор выделит 40 байтов стека вместо 24.
Это связано с тем, что компилятор сохраняет общий размер кадра стека (все, что находится слева на приведенной выше диаграмме, за исключением входящего параметра, который действительно находится внизу кадра стека вызывающего), округлен до кратного 16 байт. (См. Документацию gcc для опции -mpreferred-stack-boundary
.)