Понимание распределения и выравнивания стека

Я пытаюсь понять, как работает выравнивание стека, как описано в что такое "выравнивание стека"? но мне трудно получить небольшой пример, чтобы продемонстрировать указанное поведение. Я изучаю распределение стека моей функции foo:

void foo() {
    int a = 0;
    char b[16];
    b[0] = 'a';
}

я скомпилировал исходный файл с gcc -ggdb example.c -o example.out (i.e без каких-либо флагов компилятора) и дамп ассемблера из gdb читает:

(gdb) disassemble foo
Dump of assembler code for function foo:
0x08048394 <+0>:    push   %ebp
0x08048395 <+1>:    mov    %esp,%ebp
0x08048397 <+3>:    sub    x20,%esp
0x0804839a <+6>:    movl   x0,-0x4(%ebp)
0x080483a1 <+13>:   movb   x61,-0x14(%ebp)
0x080483a5 <+17>:   leave  
0x080483a6 <+18>:   ret    
End of assembler dump.

мой стек выделяется кусками по 16 байт (я проверил это несколькими других анализов.) Согласно дампу ассемблера, здесь было выделено 32 байта, потому что (16

редактировать: я пришел к выводу, что мой стек выделяется блоками по 16 байт с программой как показано ниже:

void foo() {
    char a[1];
}

и соответствующий дамп ассемблера:

(gdb) disassemble foo
Dump of assembler code for function foo:
0x08048394 <+0>:    push   %ebp
0x08048395 <+1>:    mov    %esp,%ebp
0x08048397 <+3>:    sub    x10,%esp
0x0804839a <+6>:    leave  
0x0804839b <+7>:    ret    
End of assembler dump.

вы можете видеть, что в стеке выделено 16 байтов для символьного массива размером 1 (требуется только 1 байт). я могу увеличить размер массива до 16, и дамп ассемблера останется прежним, но когда он равен 17, он выделяет 32 байта в стеке. Я запустили много таких образцов, и результат тот же; память стека выделяется кусками по 16 байт. Аналогичная тема обсуждалась в распределение стога, прокладка, и выравнивание но я больше заинтересован в том, чтобы выяснить, почему выравнивание не влияет на мой пример.

5 ответов


Я думаю, вам не хватает того факта, что нет требования, чтобы все переменные стека были индивидуально выровнены по 16-байтовым границам.


обычным правилом является то, что переменные выделяются на 32-бит. Я не уверен, почему вы думаете, что 16 байт имеет особое значение.


Я никогда не слышал о такой вещи, как конкретные выравнивания стека. Если есть требования к выравниванию для ЦП, выравнивание выполняется на всех видах памяти данных, независимо от того, хранится ли она в стеке или в другом месте. Он начинает на четных адресах с 16, 32 или 64 битами данных следовать.

16 байт, возможно, могут быть своего рода оптимизацией кэш-памяти на чипе, хотя это кажется мне немного надуманным.


хороший пример-увидеть это на структуре.

struct a{
    int a;
    char b;
    int c;
} a;

в 32-битной системе это будет 4+1+4 байты, если брать отдельно.

потому что структура и ее члены выровнены "char b" будет 4 байта, принимая это до 12 байтов.

struct b{
    int a;
    char b;
    int c;
} __attribute__((__packed__)) b;

используя атрибут packed, вы можете заставить его сохранить минимальный размер. Таким образом, эта структура занимает 9 байт.

Вы можете проверить это также http://sig9.com/articles/gcc-packed-structures

надеюсь, что это помогает.


вы можете проверить, как выделяется дополнительная память для вашей структуры данных с помощью инструмента под названием pahole http://packages.debian.org/lenny/dwarves . Он показывает вам все отверстия вашей программы: размер ваших данных, если вы суммируете его, и реальный размер, выделенный на вашем stuck