Uważam, że problem polega na tym, że twoja tablica znajduje się na stosie, a twój kompilator jest zbyt stary, aby obsługiwać nadmiernie wyrównane zmienne stosu. GCC 4.6 i nowsze poprawiły ten błąd .
C11 / C ++ 11 alignas(64) float a[4];
Po prostu działa dla każdej potęgi wyrównania 2.
Tak robi GNU C.__attribute__((aligned(x)))
jak go używałeś.
(W C11 #include <stdalign.h>
dla #define alignas _Alignas
: cppref ).
Ale w przypadku bardzo dużego wyrównania, do granicy 4k stron, możesz nie chcieć, aby znajdował się na stosie.
Ponieważ wskaźnik stosu może być dowolny podczas uruchamiania funkcji, nie ma sposobu na wyrównanie tablicy bez przydzielenia o wiele więcej niż potrzebujesz i dostosowania jej. (Kompilatorzy to zrobiąand rsp, -4096
lub równoważne i nie używają żadnego z przydzielonych bajtów od 0 do 4088; rozgałęzianie na to, czy ta przestrzeń jest wystarczająco duża, czy nie, byłoby możliwe, ale nie jest wykonywane, ponieważ ogromne wyrównania są znacznie większe niż rozmiar tablicy lub innych lokalnych nie są normalnym przypadkiem).
Jeśli przeniesiesz tablicę z funkcji do zmiennej globalnej, to powinno działać. Inną rzeczą, którą możesz zrobić, to zachować ją jako zmienną lokalną (co jest bardzo dobrą rzeczą), ale ją zrób static
. Zapobiegnie to przechowywaniu go na stosie. Pamiętaj, że oba te sposoby nie są bezpieczne dla wątków ani rekursji, ponieważ będzie tylko jedna kopia tablicy.
Za pomocą tego kodu:
#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]);
}
Rozumiem:
0x804c000 0x804c004 0x804c008 0x804c00c
co jest oczekiwane. Z twoim oryginalnym kodem po prostu otrzymuję losowe wartości, tak jak ty.
alignas(64)
lub cokolwiek innego na obiektach z automatycznym przechowywaniem. I oczywiście GNU C__attribute((aligned((64)))
Wystąpił błąd w gcc, który powodował, że atrybut wyrównany nie działał ze zmiennymi stosu. Wydaje się, że problem został rozwiązany za pomocą poprawki, do której link znajduje się poniżej. Poniższy link zawiera również sporo dyskusji na temat problemu.
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16660
Wypróbowałem twój kod powyżej z dwiema różnymi wersjami gcc: 4.1.2 z pudełka RedHat 5.7 i zawiódł podobnie jak twój problem (lokalne tablice nie były w żaden sposób wyrównane do granic 0x1000 bajtów). Następnie wypróbowałem twój kod z gcc 4.4.6 na RedHacie 6.3 i działał bezbłędnie (lokalne tablice zostały wyrównane). Ludzie z Myth TV mieli podobny problem (który wydaje się naprawiać powyższa łatka gcc):
http://code.mythtv.org/trac/ticket/6535
W każdym razie wygląda na to, że znalazłeś błąd w gcc, który wydaje się być naprawiony w późniejszych wersjach.
źródło
memalign()
?Najnowsze GCC (testowane z 4.5.2-8ubuntu4) wydaje się działać zgodnie z oczekiwaniami z prawidłowo wyrównaną tablicą.
#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]); }
Dostaję:
0x7ffffffefff0 0x7ffffffefff4 0x7ffffffefff8 0x7ffffffefffc 0x7ffffffef000 0x7ffffffef004 0x7ffffffef008 0x7ffffffef00c 0x7ffffffe0000 0x7ffffffe0004 0x7ffffffe0008 0x7ffffffe000c
źródło
Wyrównanie nie jest skuteczne dla wszystkich typów. Powinieneś rozważyć użycie struktury, aby zobaczyć atrybuty w akcji:
#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]); }
A potem przeczytasz:
0x603000 0x604000 0x605000 0x606000
Tego się spodziewałeś.
Edycja: Przesłane przez @yzap i po komentarzu @Caleb Case, początkowy problem dotyczy tylko wersji GCC . Sprawdziłem GCC 3.4.6 vs GCC 4.4.1 z kodem źródłowym żądającego:
$ ./test_orig-3.4.6 0x7fffe217d200 0x7fffe217d204 0x7fffe217d208 0x7fffe217d20c $ ./test_orig-4.4.1 0x7fff81db9000 0x7fff81db9004 0x7fff81db9008 0x7fff81db900c
Jest teraz oczywiste, że starsze wersje GCC (gdzieś przed 4.4.1) wykazują patologie dopasowania.
Uwaga 1: Proponowany przeze mnie kod nie odpowiada na pytanie, które rozumiałem jako „wyrównanie każdego pola tablicy”.
Uwaga 2: Wprowadzenie niestatycznego a [] do main () i kompilacja z GCC 3.4.6 łamie dyrektywę wyrównania tablicy struct, ale utrzymuje odległość 0x1000 między strukturami ... nadal źle! (zobacz odpowiedź @zifre dla obejść)
źródło