Я читаю о заполнении структур в C здесь: http://www.catb.org/esr/structure-packing/.
Я не понимаю, почему заполнение, определенное во время компиляции для переменных / структур, размещенных в стеке, является семантически допустимым во всех случаях. Приведу пример. Скажем, у нас есть код игрушки, который нужно скомпилировать:
int main() {
int a;
a = 1;
}
На X86-64 gcc -S -O0 a.c
генерирует эту сборку (лишние символы удалены):
main:
pushq %rbp
movq %rsp, %rbp
movl $1, -4(%rbp)
movl $0, %eax
popq %rbp
ret
В этом случае, почему мы знаем, что значение %rbp
и, следовательно, %rbp-4
является 4-выровненным, чтобы быть подходящим для хранения / загрузки int?
Давайте попробуем тот же пример со структурами.
struct st{
char a;
int b;
}
Из прочтения я сделал вывод, что версия структуры с дополнениями выглядит примерно так:
struct st{
char a; // 1 byte
char pad[3]; // 3 bytes
int b; // 4 bytes
}
Итак, второй пример игрушки
int main() {
struct st s;
s.a = 1;
s.b = 2;
}
генерирует
main:
pushq %rbp
movq %rsp, %rbp
movb $1, -8(%rbp)
movl $2, -4(%rbp)
movl $0, %eax
popq %rbp
ret
И мы видим, что это действительно так. Но опять же, какова гарантия того, что само значение rbp
в произвольном кадре стека будет правильно выровнено? Разве значение rbp
не доступно только во время выполнения? Как компилятор может выровнять элементы структуры, если во время компиляции ничего не известно о выравнивании начального адреса структуры?
call
. Он смещен на 8, когда достигается первая инструкция функции, потому что адрес возврата был помещен в стек. Выталкивание RBP приводит к выравниванию стека по 16-байтовой границе. - person Michael Petch   schedule 09.05.2019