Comprendre les Tableaux Flexibles (Flexible Array Members) en C

Introduit avec la norme C99, le concept de Flexible Array Member (FAM) est une fonctionnalité puissante permettant de définir une structure dont le dernier élément est un tableau de taille indéterminée. Cela offre une flexibilité précieuse pour la gestion de données de longueur variable au sein d'un seul bloc mémoire.

Définition et Syntaxe

Un tableau flexible est déclaré en laissant les crochets vides à la fin d'une structure. Contrairement à un tableau classique, sa taille n'est pas fixée à la compilation.

#include <stdio.h>

struct Record {
    int id;
    double observations[]; // Membre de tableau flexible
};

Il existe quelques règles fondamentales pour utiliser cette structure :

  • Le tableau flexible doit impérativement être le dernier membre de la structure.
  • La structure doit comporter au moins un autre membre avant le tableau flexible.
  • L'opérateur sizeof renvoie uniquement la taille de la partie fixe de la structure, ignorant le tableau flexible.

Comportement de sizeof

L'exemple suivant illustre comment le compilateur calcule l'espace mémoire d'une telle structure :

#include <stdio.h>

struct Sample {
    int header;
    char payload[];
};

int main() {
    struct Sample s;
    printf("Taille de la structure : %zu octets\n", sizeof(s)); 
    // Affiche généralement 4 (la taille d'un int), le tableau n'étant pas comptabilisé.
    return 0;
}

Allocation dynamique de mémoire

Puisque le tableau n'a pas de taille définie, nous devons allouer manuellement l'espace nécessaire sur le tas (heap) en utilisant malloc. L'espace total doit correspondre à la taille de la structure fixe plus l'espace souhaité pour le tableau.

#include <stdlib.h>

int main() {
    int nb_elements = 10;
    // Allocation : Taille fixe + espace pour 10 doubles
    struct Record *ptr = malloc(sizeof(struct Record) + nb_elements * sizeof(double));

    if (ptr != NULL) {
        ptr->id = 1;
        for (int i = 0; i < nb_elements; i++) {
            ptr->observations[i] = i * 1.5;
        }
    }

    free(ptr);
    return 0;
}

Redimensionnement avec realloc

L'un des avantgaes majeurs est la possibilité de redimensionner dynamiquement le tableau via realloc, tout en conservant la structure en un seul bloc continu.

int nouveaux_elements = 20;
struct Record *temp = realloc(ptr, sizeof(struct Record) + nouveaux_elements * sizeof(double));

if (temp != NULL) {
    ptr = temp;
}

Alternative par poinetur vs Tableau Flexible

Il est courant de comparer cette approche à l'utilisation d'un simple pointeur au sein d'une structure :

struct Alternative {
    int id;
    double *data; // Pointeur classique
};

Bien que fonctionnelle, l'approche par pointeur présente plusieurs inconvénients par rapport au tableau flexible :

  • Gestion de la mémoire : Avec un pointeur, deux allocations sont nécessaires (une pour la structure, une pour les données). Cela implique également deux appels à free dans un ordre précis.
  • Localité des données : Le tableau flexible garantit que les données sont contiguës à l'en-tête de la structure. Cela améliore les performances grâce à une meilleure utilisation de la mémoire cache (principe de localité spatiale).
  • Fragmentation : En effectuant une seule allocation au lieu de deux, on réduit la fragmentation de la mémoire heap.

Libération de la mémoire

Pour un tableau flexible, la libération est simple : un seul appel à free(ptr) suffit à libérer l'intégralité de la structure et de ses données. Il est ensuite recommandé de mettre le pointeur à NULL pour éviter les accès résiduels (dangling pointers).

free(ptr);
ptr = NULL;

Étiquettes: C99 Memory Management Data Structures Dynamic Allocation C Programming

Publié le 31 mai à 16h39