Я играл, чтобы посмотреть, как мой компьютер работает под капотом. Меня интересует то, что происходит в стеке внутри функции. Для этого я написал следующую игрушечную программу:
#include <stdio.h>
void __cdecl Test1(char a, unsigned long long b, char c)
{
char c1;
unsigned long long b1;
char a1;
c1 = 'b';
b1 = 4;
a1 = 'r';
printf("%d %d - %d - %d %d Total: %d\n",
(long)&b1 - (long)&a1, (long)&c1 - (long)&b1,
(long)&a - (long)&c1,
(long)&b - (long)&a, (long)&c - (long)&b,
(long)&c - (long)&a1
);
};
struct TestStruct
{
char a;
unsigned long long b;
char c;
};
void __cdecl Test2(char a, unsigned long long b, char c)
{
TestStruct locals;
locals.a = 'b';
locals.b = 4;
locals.c = 'r';
printf("%d %d - %d - %d %d Total: %d\n",
(long)&locals.b - (long)&locals.a, (long)&locals.c - (long)&locals.b,
(long)&a - (long)&locals.c,
(long)&b - (long)&a, (long)&c - (long)&b,
(long)&c - (long)&locals.a
);
};
int main()
{
Test1('f', 0, 'o');
Test2('f', 0, 'o');
return 0;
}
И это выплевывает следующее:
9 19 - 13 - 4 8 Итого: 53
8 8 - 24 - 4 8 Итого: 52
Аргументы функции ведут себя хорошо, но, как указано в соглашении о вызовах, я бы ожидал этого. Но локальные переменные немного странны. Мой вопрос: почему они не будут такими же? Второй вызов создает более компактный и лучше выровненный стек.
Глядя на ASM не поразительно (по крайней мере, для меня), так как адреса переменных все еще там псевдонимы. Поэтому я думаю, что это действительно вопрос о том, как ассемблер сам выделяет стек локальным переменным.
Я понимаю, что любой конкретный ответ, скорее всего, будет зависеть от платформы. Меня больше интересует общее объяснение, если только эта причуда не зависит от конкретной платформы. Для справки: я собираю с VS2010 на 64-битной машине Intel.