我在堆栈上分配12个字符串,或者相当于12个字节。
使用gdb(在Linux下)进行解密显示,要在堆栈上为字符串分配空间,esp会移动-24(向下移动)。
push %ebp
mov %esp,%ebp
sub $0x18,%esp
为什么它被移动了24(0x18)?
答案 0 :(得分:6)
如果您的函数调用其他参数,其中一些可能是传出参数的空间;其中一些可能是从寄存器中溢出的价值的临时空间;其中一些可能是填充。它将非常依赖于编译器版本和优化标志。
以下是一些简单的无意义代码:
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个字节。 (请参阅-mpreferred-stack-boundary
选项的gcc文档。)
答案 1 :(得分:2)
因为除了字符串之外的其他东西都存储在堆栈中,例如其他局部变量或临时变量。当您有复杂的表达式时,编译器存储有时会将这些表达式的中间结果存储在堆栈的内存中(特别是在禁用优化时),即使它们与显式局部变量不对应。