Являются ли переменные стека согласованными GCC __ атрибутом __ ((aligned (x)))?

У меня есть следующий код:

#include <stdio.h>

int
main(void)
{
        float a[4] __attribute__((aligned(0x1000))) = {1.0, 2.0, 3.0, 4.0};
        printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
}

И у меня есть следующий вывод:

0x7fffbfcd2da0 0x7fffbfcd2da4 0x7fffbfcd2da8 0x7fffbfcd2dac

Почему адрес a[0] не кратен 0x1000?

Что именно делает __attribute__((aligned(x)))? Я неправильно понял это объяснение?

Я использую gcc 4.1.2.

Ответ 1

Я считаю, что проблема в том, что ваш массив находится в стеке. Поскольку указатель стека может быть чем угодно, когда запускается функция, нет способа выровнять массив без выделения намного большего, чем вам нужно, и его корректировки. Если вы переместите массив из функции и в глобальную переменную, она должна работать. Другая вещь, которую вы могли бы сделать, - сохранить ее как локальную переменную (что очень хорошо), но сделать ее static. Это предотвратит его хранение в стеке. Помните, что оба эти способа не являются потокобезопасными или рекурсивно-безопасными, так как будет только одна копия массива.

С помощью этого кода:

#include <stdio.h>

float a[4] __attribute__((aligned(0x1000))) = {1.0, 2.0, 3.0, 4.0};

int
main(void)
{
        printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
}

Я получаю это:

0x804c000 0x804c004 0x804c008 0x804c00c

что и есть. С вашим исходным кодом я просто получаю случайные значения, как вы.

Ответ 2

В gcc произошла ошибка, из-за которой атрибут совпадал, чтобы не работать со стековыми переменными. По-видимому, исправлено исправление, связанное ниже. В приведенной ниже ссылке также содержится довольно небольшое обсуждение проблемы.

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16660

Я пробовал ваш код выше с двумя разными версиями gcc: 4.1.2 от RedHat 5.7 , и он сработал аналогично вашей проблеме (локальные массивы никоим образом не выровнены по границам 0x1000 байтов). Затем я попробовал свой код с gcc 4.4.6 на RedHat 6.3, и он работал безупречно (локальные массивы были выровнены). У миф-ТВ-пользователей была аналогичная проблема (что исправление gcc выше, похоже, исправить):

http://code.mythtv.org/trac/ticket/6535

В любом случае, похоже, что вы обнаружили ошибку в gcc, которая, как представляется, исправлена ​​в более поздних версиях.

Ответ 3

Недавний GCC (протестированный с 4.5.2-8ubuntu4), похоже, работает так, как ожидалось, с правильным выравниванием массива.

#include <stdio.h>

int main(void)
{
    float a[4] = { 1.0, 2.0, 3.0, 4.0 };
    float b[4] __attribute__((aligned(0x1000))) = { 1.0, 2.0, 3.0, 4.0 };
    float c[4] __attribute__((aligned(0x10000))) = { 1.0, 2.0, 3.0, 4.0 };

    printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
    printf("%p %p %p %p\n", &b[0], &b[1], &b[2], &b[3]);
    printf("%p %p %p %p\n", &c[0], &c[1], &c[2], &c[3]);
}

Я получаю:

0x7ffffffefff0 0x7ffffffefff4 0x7ffffffefff8 0x7ffffffefffc
0x7ffffffef000 0x7ffffffef004 0x7ffffffef008 0x7ffffffef00c
0x7ffffffe0000 0x7ffffffe0004 0x7ffffffe0008 0x7ffffffe000c

Ответ 4

Выравнивание не действует для всех типов. Вы должны рассмотреть возможность использования структуры для просмотра атрибутов в действии:

#include <stdio.h>

struct my_float {
        float number;
}  __attribute__((aligned(0x1000)));

struct my_float a[4] = { {1.0}, {2.0}, {3.0}, {4.0} };

int
main(void)
{
        printf("%p %p %p %p\n", &a[0], &a[1], &a[2], &a[3]);
}

И тогда вы будете читать:

0x603000 0x604000 0x605000 0x606000

Это то, чего вы ожидали.

Изменить: Выдвинутый @yzap и следующий комментарий @Caleb Case, исходная проблема связана с версией GCC только. Я проверил GCC 3.4.6 и GCC 4.4.1 с исходным кодом запроса:

$ ./test_orig-3.4.6
0x7fffe217d200 0x7fffe217d204 0x7fffe217d208 0x7fffe217d20c
$ ./test_orig-4.4.1
0x7fff81db9000 0x7fff81db9004 0x7fff81db9008 0x7fff81db900c

Теперь очевидно, что более старые версии GCC (где-то до 4.4.1) показывают патологию выравнивания.

Примечание 1: Мой предложенный код не отвечает на вопрос, который я понял как "выравнивание каждого поля массива".

Примечание 2: Приведение нестатического a [] внутри main() и компиляция с помощью GCC 3.4.6 нарушает директиву выравнивания массива структуры, но сохраняет расстояние между структурами 0x1000... все еще плохо! (см. ответ @zifre для обходных решений)