Выровнены ли переменные стека __attribute GCC __((выровненный (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.
4 ответа
Я считаю, что проблема в том, что ваш массив находится в стеке. Поскольку указатель стека может быть любым при запуске функции, невозможно выровнять массив, не выделив намного больше, чем нужно, и отрегулировав его. Если вы переместите массив из функции в глобальную переменную, он должен работать. Другая вещь, которую вы могли бы сделать, это сохранить ее как локальную переменную (что очень хорошо), но сделать это 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
что и ожидается. С вашим исходным кодом я просто получаю случайные значения, как вы.
В 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, и он работал безупречно (локальные массивы были выровнены). У ребят из Myth TV была похожая проблема (похоже, что исправление gcc выше исправило):
http://code.mythtv.org/trac/ticket/6535
В любом случае, похоже, что вы нашли ошибку в gcc, которая, по-видимому, исправлена в более поздних версиях.
Недавний 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
Выравнивание не эффективно для всех типов. Вы должны рассмотреть возможность использования структуры, чтобы увидеть атрибуты в действии:
#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 нарушает директиву выравнивания массива struct, но сохраняет расстояние 0x1000 между структурами... все еще плохо! (см. @zifre ответ для обходных путей)