Основной вопрос выравнивания данных - PullRequest
2 голосов
/ 26 декабря 2010

Я играл, чтобы посмотреть, как мой компьютер работает под капотом. Меня интересует то, что происходит в стеке внутри функции. Для этого я написал следующую игрушечную программу:

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

Ответы [ 2 ]

1 голос
/ 26 декабря 2010

Структура памяти структур POD в значительной степени определяется и гарантируется языковыми правилами + требованиями к выравниванию / размеру типа на вашей платформе.

Реализация имеет свободные руки с локальными переменными и параметрами функции. Скорее всего, некоторые из них помещаются в стек только потому, что вы используете унарный оператор & для получения их адресов.

Когда локальная переменная не используется, компилятор может оптимизировать ее инициализацию. Когда локальная простая переменная используется очень интенсивно, компилятор может использовать для нее регистр. Когда локальная переменная используется только один раз, компилятор может использовать ее значение непосредственно вместо использования.

Если вы хотите, чтобы компоновка параметров функции была лучше задана / гарантирована, вам нужно использовать внешнюю связь "C".

0 голосов
/ 26 декабря 2010

В Test1 вы определяете группу локальных переменных.Компилятор не обязан упаковывать их вместе или в том порядке, в котором вы их объявляете.

В Test2 вы определяете структуру и данные панели компилятора, используя http://en.wikipedia.org/wiki/Data_structure_alignment#Typical_alignment_of_C_structs_on_x86

...