在堆栈上分配String

时间:2010-11-24 19:32:52

标签: c assembly

我在堆栈上分配12个字符串,或者相当于12个字节。

使用gdb(在Linux下)进行解密显示,要在堆栈上为字符串分配空间,esp会移动-24(向下移动)。

push   %ebp
mov    %esp,%ebp
sub    $0x18,%esp

为什么它被移动了24(0x18)?

2 个答案:

答案 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)

因为除了字符串之外的其他东西都存储在堆栈中,例如其他局部变量或临时变量。当您有复杂的表达式时,编译器存储有时会将这些表达式的中间结果存储在堆栈的内存中(特别是在禁用优化时),即使它们与显式局部变量不对应。