« Programmation C/Types avancés » : différence entre les versions

Contenu supprimé Contenu ajouté
→‎Manipulation : Suppression d'une phrase sans grand sens...
→‎Alignement et bourrage (padding) : Clarifications (enfin, j'espère)
Ligne 71 :
 
=== Alignement et bourrage (padding) ===
Il s'agit d'un concept relativement avancé, mais qu'il est bien de connaitre pour agir en connaissance de cause. Lorsqu'on déclare une structure, on pourrait naïvement croire que les champs se suivent les uns à la suite des autres en mémoire. ConsidérezOr, il arrive souvent qu'une partie de la structuremémoire ne soit pas suivante :utilisée.
 
Considérez que nous travaillons sur un environnement où le « mot machine » fait 4 octets, et où un <tt>char</tt> occupe un octet, et <tt>int</tt> 4. considérez maintenant la structure suivante:
 
<source lang="c">
struct ma_structure {
char champ1; /* 8bits 8 bits */
int champ2; /* 32bits32 bits */
char champ3; /* 8bits 8 bits */
};
</source>
 
On pourrait penser que cette structure fasseoccupera 6 octets en mémoire, et pourtant, sur la majeure partie des compilateurs, pour ne pas dire tous, on obtiendrait une taille de 12 octets.
 
En fait, les compilateurs insèrent quasi toujours des octets entre les champs pour pouvoir les ''aligner'' sur undes adressageadresses pair,qui oucorrespondent multipleà dedes 4 ou demots 8machines. C'est en fait dû à une limitation de la plupart des processeurs, qui ne peuvent lire des mots de plus d'un octet que s'ils sont alignés sur un certain adressage. En fait, toutes les variables déclarées suivent cette règle : aussi bien les variables locales aux fonctions, les champs de structures, les paramètres de fonctions, etc.
 
CetteCet quantité de bourragecomportement est en fait non seulement dépendantedépendant de l'architecture, mais aussi du compilateur. Ce dernier possède en général des options qui permettent de paramétrer avec quelle finesse se fera l'alignement. Ces options sont bien évidemment spécifiques et pas du tout portables.
 
Dans le cas d'un ordinateur dit « 32 bits », par exemple (i.e. dont le processeur central utilise des ''registres'' de 32 bits), il est courant que la mémoire ne puisse être adressée directement que par « bloc » de 32 bits. En se représentant la mémoire comme un tableau continu, on peut tracer le dessin suivant:
 
<pre>
Ligne 99 ⟶ 101 :
Un autre compilateur (ou le même, appelé avec des options différentes) peut aussi placer <code>champ2</code> de <code>b</code> à <code>e</code>, et <code>champ3</code> en <code>f</code>, pour optimiser l'utilisation mémoire. Mais alors il devra générer un code plus complexe lors des accès à <code>champ2</code>, le matériel ne lui permettant pas de le faire directement.
 
Même si les options commandant l'utilisation de bourrage sont spécifiques à chaque compilateur, il peut s'avérer nécessaire de connaître la « distance » (''offset'') d'un champ par rapport au début de la structure, et ce, de manière portable. Pour cela il existe une fonction (en fait, macro), déclarée dans l'entête <code>stddef.h</code>:
 
<source lang="c">
Ligne 105 ⟶ 107 :
</source>
 
La valeur renvoyée est le nombre de mots machines<tt>char</tt> (i.e. d'octets la plupart du temps), entre le début de la structure et celui du champ. Le premier argument attendu est bel et bien le ''type'' de la structure et non unune pointeurvariable versde ce type, (avecni un <code>typedef</code>pointeur àvers laune limite,variable dude moment que lece type déclaré n'est pas un pointeur). Pour s'en souvenir, il suffit de savoir que beaucoup d'implémentations d'<code>offsetof</code> utiliseutilisent une arithmétique de ce genre:
 
<source lang="c">
Ligne 111 ⟶ 113 :
</source>
 
Si <code>type</code> était un pointeur, il faudrait faire un déréférencement supplémentaire (ou éviter l'étoile dans la macro). À noter que, même si cette macro peut s'avérer contraignante (notamment lorsqu'on ne dispose que de type pointeur), il est quand même préférable de l'utiliser pour des raisons de portabilité.
 
Voici un petit exemple d'utilisation de cette macro&nbsp;:
 
<source lang="c">
Ligne 120 ⟶ 122 :
 
struct ma_structure {
char champ1; /* 8bits */
int champ2; /* 32bits */
char champ3; /* 8bits */
};
 
Ligne 136 ⟶ 138 :
</source>
 
Sur une architecture 32bits32 bits, vous obtiendrez très certainement la réponse:
<pre>L'offset de 'champ2' vaut 4.</pre>