Не существует ни одного соглашения о вызовах x86, и расположение локальных переменных в любом случае не определено соглашением о вызовах.
Таким образом, вопрос сформулирован неправильно, даже до того, как будет учтено, как реализованы типы C.
Если предположить:
stdcall
или cdecl
соглашение о вызовах. - Компилятор использует указатель кадра.
- Компилятор будет выравнивать данные, по крайней мере, по своим естественным границам.
short
, int
, long
равны 16, 32, 32 битам соответственно и выровнены по 2, 4 и 4 байтам соответственно.
тогда я ожидал бы этот стек (обратите внимание, что я использую шестнадцатеричное представление):
[EBP +10h] c
[EBP +0Ch] b
[EBP +08h] a
[EBP +04h] Return address
[EBP +00h] Old EBP
[EBP -02h] q
[EBP -05h] s
[EBP -0ch] y
Вот что Clang делает без оптимизаций за исключением того, что он сохраняет 3 регистратем самым сдвигая смещения вниз на 12.
a
, b
и c
помещаются в стек в обратном порядке, все типы должны занимать число байтов, кратное 4.
Местное размещение не является абсолютнымТо есть, это зависит от окружающего кода (использование энергонезависимых регистров, вызовов, временных переменных, защиты стека и т. д.), и в этом случае, когда вообще нет кода или контекста, вопрос немного неясен.
Используя только последние два предположения 1 , мы можем поместить q
в [EBP -02h]
, поскольку q
составляет 2 байта и должно быть по адресу, кратному 2;s
имеет значение [EBP -05h]
, поскольку оно составляет 3 байта и выровнено по байту;наконец, y
может перейти в [EBP -09h]
с точки зрения только размера, но так как он должен быть выровнен по 4-байтам, он идет в [EBP -0ch]
.
Обратите внимание, что все рассмотренные выше рассуждения используюттот факт, что EBP
выравнивается на 4 байта.
1 Мы также предполагаем, что местные жители расположены в обратном порядке: последний в порядке объявления размещен по более низкому адресу.