Выделение строки в стеке - PullRequest
1 голос
/ 24 ноября 2010

Я выделяю 12 символов строки в стеке, или, что эквивалентно, это 12 байтов.

Демонстрация с помощью gdb (под Linux) показывает, что для выделения места для строки в стеке esp перемещается на -24 (вниз по стеку).

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

Почему он перемещается24 (0x18)?

Ответы [ 2 ]

6 голосов
/ 24 ноября 2010

Некоторые из них могут быть пространством для исходящих параметров, если ваша функция вызывает другие; часть из них может быть временным пространством для значений, вылитых из регистров; некоторые из них могут быть дополнением. Это будет очень зависеть от версии компилятора и флагов оптимизации.

Вот несколько простых бессмысленных кодов для иллюстрации:

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.)

2 голосов
/ 24 ноября 2010

Потому что другие вещи помимо вашей строки хранятся в стеке, такие как другие локальные переменные или временные значения. Если у вас сложные выражения, компилятор иногда сохраняет промежуточные результаты этих выражений в памяти в стеке (особенно, когда оптимизация отключена), даже если они не соответствуют явным локальным переменным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...