89 votes

Les variables de la pile sont-elles alignées par l'attribut __attribut__((aligné(x)) du GCC ?)

J'ai le code suivant :

#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]);
}

Et j'ai le résultat suivant :

0x7fffbfcd2da0 0x7fffbfcd2da4 0x7fffbfcd2da8 0x7fffbfcd2dac

Pourquoi l'adresse de a[0] n'est pas un multiple de 0x1000 ?

Quoi exactement ? __attribute__((aligned(x))) fait ? J'ai mal compris. este explication ?

J'utilise gcc 4.1.2.

100voto

Zifre Points 14109

Je pense que le problème est que votre tableau est sur la pile, et que votre compilateur est trop vieux pour supporter les variables de pile sur-alignées. GCC 4.6 et suivants correction de ce bug .

C11/C++11 alignas(64) float a[4]; Cela fonctionne pour tout alignement de puissance 2.
Il en va de même pour le GNU C __attribute__((aligned(x))) pendant que vous l'utilisiez.

(En C11, #include <stdalign.h> pour le #define alignas _Alignas : cppref ).


Mais dans votre cas d'un très grand alignement, sur une frontière de page de 4k, vous ne voulez peut-être pas le mettre sur la pile.

Comme le pointeur de la pile peut être n'importe où au démarrage de la fonction, il n'y a aucun moyen d'aligner le tableau sans allouer beaucoup plus que nécessaire et l'ajuster. (Les compilateurs and rsp, -4096 ou équivalent et n'utiliser aucun des 0 à 4088 octets qui ont été alloués ; bifurquer sur le fait de savoir si cet espace est assez grand ou non serait possible mais n'est pas fait car les alignements énormes beaucoup plus grands que la taille du tableau ou d'autres locaux ne sont pas le cas normal).

Si vous déplacez le tableau hors de la fonction et dans une variable globale, cela devrait fonctionner. L'autre possibilité est de le conserver comme variable locale (ce qui est une très bonne chose), mais de le rendre static . Cela l'empêchera d'être stocké sur la pile. Attention, ces deux méthodes ne sont pas sûres pour les threads ou les récursions, puisqu'il n'y aura qu'une seule copie du tableau.

Avec ce code :

#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]);
}

Je comprends :

0x804c000 0x804c004 0x804c008 0x804c00c

ce qui est attendu. Avec votre code original, j'obtiens juste des valeurs aléatoires comme vous l'avez fait.

12 votes

+1 bonne réponse. Une autre solution consiste à rendre le tableau local statique. L'alignement sur la pile est toujours un problème et il est préférable de prendre l'habitude de l'éviter.

0 votes

Oh oui, je n'ai pas pensé à le rendre statique. C'est une bonne idée car cela évite les collisions de noms. Je vais modifier ma réponse.

3 votes

Notez que le fait de le rendre statique le rend également non-réentrant et non-thread-safe.

42voto

rts1 Points 286

Il y avait un bug dans gcc qui causait attribut aligné pour ne pas fonctionner avec les variables de la pile. Il semble être corrigé avec le patch lié ci-dessous. Le lien ci-dessous contient également un certain nombre de discussions sur le problème.

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

J'ai essayé votre code ci-dessus avec deux versions différentes de gcc : 4.1.2 sur une machine RedHat 5.7. et cela a échoué de la même manière que votre problème (les tableaux locaux n'étaient en aucun cas alignés sur des limites de 0x1000 octets). J'ai ensuite essayé votre code avec gcc 4.4.6 sur RedHat 6.3, et il a fonctionné sans problème (les tableaux locaux étaient alignés). Les gens de Myth TV ont eu un problème similaire (que le patch gcc ci-dessus a semblé résoudre) :

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

Quoi qu'il en soit, il semble que vous ayez trouvé un bogue dans gcc, qui semble être corrigé dans les versions ultérieures.

3 votes

Selon le bogue lié, gcc 4.6 était la première version avec ce problème entièrement corrigé pour toutes les architectures.

0 votes

En plus de cela, le code d'assemblage généré par gcc pour créer une variable alignée sur la pile est si horrible et si peu optimisé. Donc, cela a-t-il un sens d'allouer des variables alignées sur la pile au lieu d'appeler memalign() ?

13voto

Caleb Case Points 501

Les GCC récents (testés avec 4.5.2-8ubuntu4) semblent fonctionner comme prévu avec le tableau aligné correctement.

#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]);
}

J'ai compris :

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

0 votes

C'est un peu surprenant, étant donné que les tableaux sont alloués dans la pile - cela signifie-t-il que la pile est maintenant pleine de trous ?

0 votes

Ou sa pile est alignée sur 16 octets.

9voto

levif Points 887

L'alignement n'est pas efficace pour tous les types. Vous devriez envisager d'utiliser une structure pour voir les attributs en action :

#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]);
}

Et ensuite, vous lirez :

0x603000 0x604000 0x605000 0x606000

Ce qui est ce que vous attendiez.

Edit : Poussé par @yzap et suite au commentaire de @Caleb Case, le problème initial est dû à la version de GCC seulement . J'ai vérifié sur GCC 3.4.6 vs GCC 4.4.1 avec le code source du demandeur :

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

Il est maintenant évident que les anciennes versions de GCC (quelque part avant 4.4.1) présentent des pathologies d'alignement.

Note 1 : Le code que je propose ne répond pas à la question que j'ai comprise comme "aligner chaque champ du tableau".

Note 2 : Introduire des a[] non-statiques dans main() et compiler avec GCC 3.4.6 casse la directive d'alignement du tableau de struct mais garde la distance 0x1000 entre les structs... toujours mauvais ! (voir la réponse de @zifre pour les solutions de contournement)

2 votes

Comme répondu par zifre, ce n'est pas le type, mais le fait que vous l'ayez rendu statique dans votre version.

0 votes

@ysap, c'est à la fois la version de GCC et la définition globale qui ont fait que cela fonctionne. Merci pour votre commentaire ! J'ai édité la réponse pour le corriger :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X