Structures et pointeurs en C

30 Aug 2019 30 Aug 2019 17436 vues ESSADDOUKI Mostafa 8 min de lecture

Pointeurs et structures en C

Combiner pointeurs et structures est une technique fondamentale en C pour manipuler des données composites efficacement, notamment pour les listes chaînées, les arbres, et les tableaux dynamiques de structures.

Prérequis Ce cours suppose la maîtrise des pointeurs (déclaration, déréférencement, arithmétique) et des structures (struct, accès par .). Consulter les cours correspondants avant de poursuivre.

1. Déclarer un pointeur sur une structure

Pointeur sur structure Un pointeur sur structure est une variable qui stocke l'adresse mémoire d'une variable de type struct. Sa déclaration suit la même syntaxe que tout pointeur : struct NomType *ptr;.

Syntaxe C
struct NomType *ptr;          /* déclaration seule          */
struct NomType *ptr = &var;   /* déclaration + initialisation */

Exemple n°1 — Déclarer un pointeur sur struct

#include <stdio.h>

struct etudiant {
    char prenom[20];
    int  age;
};

int main(void)
{
    struct etudiant *et1;   /* pointeur sur struct etudiant — non initialisé */
    struct etudiant  et2;   /* variable struct normale                        */

    et1 = &et2;             /* et1 pointe maintenant sur et2                 */

    return 0;
}
Attention — Pointeur non initialiséUn pointeur déclaré sans initialisation pointe vers une adresse arbitraire. L'utiliser avant de lui affecter une adresse valide provoque un comportement indéfini (segmentation fault). Toujours initialiser avant usage :
struct etudiant *p;
p->age = 20;         /* Danger — p non initialisé    */

struct etudiant e;
struct etudiant *p = &e;
p->age = 20;         /* Correct — p pointe sur e     */

2. Accéder aux membres via un pointeur

Deux notations permettent d'accéder aux membres d'une structure via un pointeur :

NotationSyntaxeDescription
Flèche (recommandée)ptr->membreAccès direct via pointeur — forme idiomatique
Point + déréférencement(*ptr).membreDéréférencement puis accès — parenthèses obligatoires
Équivalence -> et (*ptr). L'opérateur -> est un raccourci syntaxique : ptr->membre est strictement équivalent à (*ptr).membre. La flèche est préférée car plus lisible et moins sujette aux erreurs de priorité des opérateurs.
et1->prenom  ≡  (*et1).prenom
et1->age     ≡  (*et1).age

/* Attention : *et1.age serait une erreur — équivaut à *(et1.age) */

Exemple n°2 — Saisie et affichage via pointeur

#include <stdio.h>

struct etudiant {
    char prenom[20];
    int  age;
};

int main(void)
{
    struct etudiant  et2;
    struct etudiant *et1 = &et2;   /* et1 pointe sur et2 */

    printf("Saisir votre prénom : ");
    scanf("%s", et1->prenom);      /* pas de & — prenom est déjà un tableau */

    printf("Saisir votre âge : ");
    scanf("%d", &et1->age);

    printf("\n--- Informations ---\n");
    printf("Prénom : %s\n", et1->prenom);
    printf("Âge    : %d\n", et1->age);

    /* Vérification : les deux notations donnent le même résultat */
    printf("\nVia (*et1).prenom : %s\n", (*et1).prenom);

    return 0;
}
Sortie
Saisir votre prénom : Mostafa
Saisir votre âge : 32

--- Informations ---
Prénom : Mostafa
Âge    : 32

Via (*et1).prenom : Mostafa
Astuce — scanf et les tableaux de caractères Pour un champ de type tableau (char prenom[20]), le nom du tableau est déjà une adresse — il ne faut pas ajouter & devant et1->prenom. Pour un champ scalaire (int age), il faut &et1->age:
scanf("%s", et1->prenom);    /* tableau — sans &  */
scanf("%d", &et1->age);      /* scalaire — avec & */

3. Allocation dynamique d'une structure

Allouer une structure sur le tas avec malloc permet de créer des structures dont la durée de vie n'est pas liée à la portée d'une fonction et de construire des structures de données dynamiques (listes chaînées, arbres…).


Syntaxe — Allocation d'une structure C
#include <stdlib.h>

struct NomType *ptr;
ptr = (struct NomType *) malloc(sizeof(struct NomType));

if (ptr == NULL) {
    /* gestion de l'échec d'allocation */
}

/* ... utilisation ... */
free(ptr);
ptr = NULL;

Exemple n°3 — Allocation dynamique d'un étudiant

#include <stdio.h>
#include <stdlib.h>

struct etudiant {
    char prenom[20];
    int  age;
};

int main(void)
{
    struct etudiant *et1;

    /* Allocation sur le tas */
    et1 = (struct etudiant *) malloc(sizeof(struct etudiant));
    if (et1 == NULL) {
        printf("Erreur d'allocation mémoire\n");
        return 1;
    }

    printf("Saisir votre prénom : ");
    scanf("%s", et1->prenom);

    printf("Saisir votre âge : ");
    scanf("%d", &et1->age);

    printf("\nPrénom : %s\n", et1->prenom);
    printf("Âge    : %d\n",   et1->age);

    /* Libération indispensable */
    free(et1);
    et1 = NULL;

    return 0;
}
Sortie
Saisir votre prénom : Mostafa
Saisir votre âge : 24

Prénom : Mostafa
Âge    : 24
Danger — Fuite mémoire et oubli de free Tout appel à malloc doit être suivi d'un appel à free lorsque la mémoire n'est plus nécessaire. Oublier le free provoque une fuite mémoire — la mémoire reste occupée jusqu'à la fin du programme. Mettre systématiquement ptr = NULL après free pour éviter le double free et l'utilisation d'un pointeur fantôme.

4. Tableau dynamique de structures

Allouer un tableau de N structures en une seule opération est le cas d'usage le plus courant des pointeurs sur structures — par exemple pour gérer une liste d'étudiants dont le nombre n'est connu qu'à l'exécution.


Syntaxe — Tableau dynamique de structures C
struct NomType *tab;
tab = (struct NomType *) malloc(n * sizeof(struct NomType));

/* Accès à l'élément i — deux notations équivalentes */
tab[i].membre          /* notation tableau — recommandée */
(tab + i)->membre      /* notation pointeur              */

Exemple n°4 — Tableau dynamique de N étudiants

#include <stdio.h>
#include <stdlib.h>

struct etudiant {
    char prenom[20];
    int  age;
};

int main(void)
{
    struct etudiant *tab;
    int n, i;

    printf("Nombre d'étudiants : ");
    scanf("%d", &n);

    /* Allocation de n structures contiguës */
    tab = (struct etudiant *) malloc(n * sizeof(struct etudiant));
    if (tab == NULL) {
        printf("Erreur d'allocation\n");
        return 1;
    }

    /* Saisie */
    for (i = 0 ; i < n ; i++) {
        printf("\n--- Étudiant N°%d ---\n", i + 1);

        printf("Prénom : ");
        scanf("%s", tab[i].prenom);         /* notation tableau */

        printf("Âge    : ");
        scanf("%d", &tab[i].age);
    }

    /* Affichage */
    printf("\n=== Liste des étudiants ===\n");
    for (i = 0 ; i < n ; i++) {
        printf("Étudiant N°%d : %s, %d ans\n",
               i + 1, tab[i].prenom, tab[i].age);
    }

    free(tab);
    tab = NULL;

    return 0;
}
Sortie
Nombre d'étudiants : 3

--- Étudiant N°1 ---
Prénom : Mostafa
Âge    : 23

--- Étudiant N°2 ---
Prénom : Ismail
Âge    : 20

--- Étudiant N°3 ---
Prénom : Dounia
Âge    : 22

=== Liste des étudiants ===
Étudiant N°1 : Mostafa, 23 ans
Étudiant N°2 : Ismail, 20 ans
Étudiant N°3 : Dounia, 22 ans
Deux notations pour accéder aux élémentsAvec un tableau dynamique de structures, les notations tableau et pointeur sont strictement équivalentes. La notation tableau est plus lisible et recommandée :
tab[i].prenom       /* notation tableau  — recommandée */
(tab + i)->prenom   /* notation pointeur — équivalente */

Exemple n°5 — Passage d'un tableau de structures en fonction

#include <stdio.h>
#include <stdlib.h>

struct etudiant {
    char prenom[20];
    int  age;
};

/* La fonction reçoit un pointeur — le tableau n'est pas copié */
void afficher(struct etudiant *tab, int n)
{
    int i;
    for (i = 0 ; i < n ; i++) {
        printf("%d. %-15s %d ans\n", i + 1, tab[i].prenom, tab[i].age);
    }
}

int main(void)
{
    struct etudiant *tab;
    int n = 3;

    tab = (struct etudiant *) malloc(n * sizeof(struct etudiant));

    /* Initialisation directe pour l'exemple */
    tab[0] = (struct etudiant){"Mostafa", 23};
    tab[1] = (struct etudiant){"Ismail",  20};
    tab[2] = (struct etudiant){"Dounia",  22};

    afficher(tab, n);

    free(tab);
    tab = NULL;
    return 0;
}
Sortie
1. Mostafa          23 ans
2. Ismail           20 ans
3. Dounia           22 ans

Récapitulatif

OpérationSyntaxeRemarque
Déclarationstruct T *p;Non initialisé — dangereux à utiliser
Affectation d'adressep = &var;p pointe sur une variable existante
Accès membre (flèche)p->membreRecommandé — idiomatique
Accès membre (point)(*p).membreÉquivalent — parenthèses obligatoires
Allocation d'une structmalloc(sizeof(struct T))Vérifier NULL + appeler free()
Tableau dynamiquemalloc(n * sizeof(struct T))Accès par tab[i].membre
Libérationfree(p); p = NULL;Obligatoire — évite fuite mémoire

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.