Alignement mémoire des structures et détection de l’endianness via les unions

Alignement mémoire et mots machine

La longueru du mot machine détermine la granularité des accès mémoire. Chaque variable possède une valeur m qui représente son exigence d’alignement.

Calcul de m pour les types simples

Sur un système 32 bits, le processeur lit la mémoire par blocs de 4 octets. Une variable est correctement alignée lorsque son adresse est un multiple d’une certaine valeur, appelée m.

  • char c; → m = 1 (toujours aligné)
  • short s; → m = 2 (adresse paire)
  • int i; → m = 4 (adresse multiple de 4)
  • double d; → m = 4 sur 32 bits (taille 8, alignement limité par le mot machine)

Attention : m n’est pas égal à la taille de la variable, bien qu’il en dépende souvent.

Exemple : structure et offsets

#include <stdio.h>
#include <stddef.h>

struct sample {
    char a;
    int  b;
    short c;
    double d;
};

int main() {
    printf("Taille totale : %zu\n", sizeof(struct sample));
    printf("Offset a : %zu\n", offsetof(struct sample, a));
    printf("Offset b : %zu\n", offsetof(struct sample, b));
    printf("Offset c : %zu\n", offsetof(struct sample, c));
    printf("Offset d : %zu\n", offsetof(struct sample, d));
    return 0;
}

Analyse du placement mémoire

  1. a commence à l’offset 0, occupe 1 octet.
  2. b nécessite un alignement sur 4 octets : on ajoute 3 octets de bourrage après a ; b se place à l’offset 4.
  3. c, aligné sur 2 octets, se trouve à l’offset 8 (immédiatement après b).
  4. d exige un alignement sur 8 octets : après c (qui occupe 2 octets, de 8 à 9), on ajoute 6 octets de bourrage pour atteindre l’offset 16.

Sur un système 64 bits avec alignement standard, la structure occupe 24 octets.

Recommandation pour minimiser la taille

Ordonner les membres par taille croissante (du plus petit au plus grand) réduit généralement l’espace perdu en bourrage.

Règles pratiques

  • 32 bits : char=1, short=2, int=4, long=4, float=4, double=8
  • 64 bits : char=1, short=2, int=4, long=8, float=4, double=8
  • Trouver le membre le plus grand : si sa taille < 8, alors m = cette taille. Sinon, m = 4 sur 32 bits, m = 8 sur 64 bits.
  • Lors du placement, on ne fractionne jamais un type de donnée.

Détection de l’endianness par union

L’endianness définit l’ordre des octets d’un mot en mémoire :

  • Big-endian : l’octet de poids fort est stocké à l’adresse la plus basse.
  • Little-endian : l’octet de poids faible est stocké à l’adresse la plus basse.

Programme de test

#include <stdio.h>

union data {
    int  val;
    char bytes[2];
};

int main() {
    union data d;
    d.bytes[0] = 10;
    d.bytes[1] = 1;
    printf("%d\n", d.val);
    return 0;
}

Explication du résultat

Supposons un int sur 4 octets et une architecture little-endian. Les octets sont placés ainsi en mémoire :

  • bytes[0] à l’adresse basse : 10 (0x0A)
  • bytes[1] à l’adresse suivante : 1 (0x01)

En little-endian, val vaut 0x010A en hexadécimal, soit en décimal :

1 * 256 + 10 = 266

La sortie est donc 266.

Étiquettes: alignement mémoire union C endianness struct padding sizeof

Publié le 25 juillet à 11h07