Dans le second cas si j'ai bien compris le pragma tu désactive l'alignement par le compilateur! Bien sûr que c'est plus compact!
Tu n'as effectivement rien compris... désolé. Le problème c'est la notion d'alignement. L'arrangement des membres d'une structure vient après.
Qu'est-ce que signifie "processeur 32 bits", c'est une question que je me pose depuis que je suis petit (mais à l'époque c'était surtout "console 16 bits"). En fait, cela peut vouloir dire plein de choses :
- que les registres font x bits
- que les registres de donnée font x bits, que l'UAL calcule sur x bits
- que les registres d'adresse font x bits, que l'espace d'adressage fait 2**x
- que le bus mémoire fait x bits, que x bits peuvent être écrit atomiquement dans la mémoire
(Attention : les affirmations séparées par des virgules ne sont pas équivalentes.)
Si tu y réfléchi, tu remarquera une discrimination implicite contre les flottants en faveur des entiers, des pointeurs, ou des registres généraux. Tu parles d'architecture 32 bits, mais sur x86 les registres flottants font 80 bits ("flottants étendus"). Pourquoi pas "processeur 80 bits"?
Que je saches (et je ne veux pas le savoir!), certains registres généraux x86 de 32 bits se divisent en 2 registres de 16 bits utilisables indépendamment. (On a alors plus de registres, sur un processeur qui en manque cruellement.) Je défendrais donc le point de vue selon lequel c'est un processeur 16 bits.
Mais sur ce même processeur (de très loin le plus étrange que je connaisse), il existe un multitude de façons d'adresser la mémoire (j'exagère à peine), dont un mode, je crois, permet d'adresser 48 bits. Va t-on parler de processeur 48 bits? (Ici on voit que l'espace mémoire adressable n'est pas équivalent à la taille des registres.)
Juste pour montrer qu'on peut tout dire et son contraire.
Plus on y réfléchi, plus on se dit que "architecture x bits", "processeur x bits" est un slogan technico-commercial qui ne veut strictement rien dire, à part que quelque part, dans le système, un traitement se fait sur x bits, ou un registre a une taille de x bits. La belle affaire!
Après, Intel 32 bits peut être une façon de différencier les processeurs compatibles 8086 et la nouvelle architecture Intel explicitement parallèle ia64. Donc un nom de produit sans plus de signification que "mt68040" (qui n'a pas 68040 transistors).
Revenons à nos moutons : qu'est-ce que l'alignement? Je vais prendre le cas des architecture d'ordinateurs personnels, à l'exclusion d'autres cas que tu n'es pas près de rencontrer.
L'alignement est une contrainte qu'impose le processeur sur les accès mémoire : il veut que l'adresse utilisée pour certains accès à plusieurs octets soit alignée.
Déjà, rappelons que l'adressage se fait au niveau des octets. C'est à dire que l'unité dans une adresse c'est l'octet : on ne peut pas désigner un bit, ou un demi-octet avec un adresse.
Il faut savoir que, même si RAM permet la modification d'un seul octet à la fois, elle est organisée par unités de 32 ou 64 bits (selon architecture). Ce qui permet aussi de lire ou d'écrire 32 ou 64 bits d'un coup, mais à condition qu'ils soient "d'un bloc", stoqués ensemble. Par exemple, pour lire un "mot", il voudra que l'accès se fasse sur un "mot mémoire" entier, pas à cheval entre deux. Les adresses multiples de cette taille sont donc au début d'un bloc qui peut être manipulé d'un seul coup. Pour lire le même nombre de bits non-alignés sur cette frontière de "blocs", il est nécessaire de faire plusieurs accès.
Bien sûr, tu ne travaille pas directement au niveau de la RAM mais au niveau du processeur. Lui aussi va imposer des contraintes d'alignement.
Si tu accèdes à un "mot", mettons de 32 bits, avec une adresse qui est correctement alignée, c'est à dire qui désigne le premier octet d'un mot mémoire, le processeur n'a qu'à faire un accès à la RAM (ou au cache, peu importe). Si au contraire l'adresse ne désigne pas le premier octet d'un mot mémoire, mais, par exemple le second, et que donc la donnée se trouve à cheval sur deux mots mémoire, certains processeurs vont considérer que c'est une erreur et le système va interrompre ton programme. D'autres (dont x86) vont permettre l'accès en récupérant les deux partie de la donnée et en les assemblant si c'est une lecture, ou bien en découpant la valeur à écrire en deux et en faisant deux écritures partielles. Ceci est deux ou trois fois plus lent qu'un accès aligné, et il n'y aucun chance pour que ça s'améliore : les concepteur de processeur n'ont aucune raison d'optimiser un code qui se moque de l'alignement. (Un tel code ne cherche manifestement pas la vitesse en premier lieu.)
Il n'est pas évident, d'ailleurs, que permettre les accès non-alignés soit un cadeau au programmeur : si ils sont le résultat d'une erreur de programmation, ou de la méconnaissance d'une notion élémentaire, mieux vaut le signaler que de tolérer, inefficacement, une telle programmation. En général, les architectures très optimisées ne vont pas s'amuser à supporter une telle programmation peu rigoureuse.
Bon, tout ceci portait sur les processeur, et donc la programmation en assembleur; évidemment tu ne programme pas en assembleur, mais en C++. Mais le C++ permet d'utiliser les opérations de base du processeur quand on utilise les types primitifs. Ainsi, int est conçu pour correspondre à un mot processeur, qu'il fasse 16, 32, 64 ou même 36 bits! Donc la contrainte d'alignement est transposée telle-quelle en C++.
Les types de base char, short, int , long, float double et long double sont censées correspondre à des fonctionnalité présentes dans le processeur. Les type structurés (structure, union), eux sont des outils de programmation qui n'ont pas de traduction directe fixée.
Une structure particulière a un alignement, pour les besoins de la cause : elle a des membres, qui sont des types primitifs (ou d'autres structures, qui contiennent des types primitifs, etc.). Ce sont ces membres qui ont besoin d'être alignés. Les structures, en tant que tel, n'ont pas d'alignement particulier.
Par exemple, il n'y a aucune raison d'aligner cette structure:
parce qu'un char, par définition, n'a pas d'alignement.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 struct non_aligné { char seul_membre; };
Si par contre une structure possède au moins un membre qui a un alignement > 1, la structure est alignée pour que le membre le soit :
Sur ton système, ça va donner un alignement de 4 pour struct_alignée. Si on ajoute d'autres membres, il faudra toujours que l'alignement soit au moins 4 (ce qui veut dire multiple de 4).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 struct struct_alignée { int seul_membre; };
Si, sur ton système "32 bits", les int font bien 32 bits et ont un alignement de 4, les double en font 64 et je suis quasiment sûr que leur alignement préféré est de 8. De même, il me semble, pour long long (ou _int64 ou je ne sais quoi). Une structure dont un membre a une alignement de 8 devra aussi avoir un alignement de 8.
Partager