Langage C/C++

Guide Complet : Double Pointeur en C

×

Recommandés

Guide : Déclarer un Pointeur en C...
Cet article vous montre comment déclarer...
En savoir plus
Guide complet : strcpy en C
La fonction strcpy en C est...
En savoir plus
Malloc contre Calloc en C : Guide...
Les fonctions malloc et calloc sont...
En savoir plus
Appeler une Fonction en C - Guide...
Appeler une fonction en C est...
En savoir plus
Structure d'un Programme en Langage C
Le langage de programmation C, développé...
En savoir plus
Création des Tables de Multiplication en Langage...
Dans cet article, nous allons...
En savoir plus

Les doubles pointeurs (ou pointeurs de pointeurs) en C sont des variables qui contiennent l’adresse d’un pointeur. Ils permettent de manipuler des pointeurs de manière flexible, et sont souvent utilisés dans des contextes avancés comme la gestion dynamique de mémoire ou la manipulation de tableaux 2D. Ce guide explique les concepts fondamentaux et leur utilisation, avec des exemples corrigés.


1. Déclaration et Syntaxe

Un double pointeur est déclaré en ajoutant un second astérisque (**) lors de la définition.

Syntaxe :

type **nom_double_pointeur;

Exemple :

int **doublePtr; // Déclare un double pointeur vers un int

Le type de doublePtr est un pointeur vers un pointeur qui pointe vers un int.


2. Concept Fondamental

Un double pointeur est utile lorsque :

  • Vous voulez pointer vers une autre variable qui est déjà un pointeur.
  • Vous devez modifier l’adresse contenue dans un pointeur à partir d’une fonction.

3. Allocation Dynamique et Double Pointeur

L’un des usages courants des doubles pointeurs est l’allocation dynamique d’un tableau 2D.

Exemple : Allocation dynamique d’un tableau 2D

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

int main() {
    int rows = 3, cols = 4;
    int **matrix = malloc(rows * sizeof(int *)); // Allocation pour les lignes

    for (int i = 0; i < rows; i++) {
        matrix[i] = malloc(cols * sizeof(int)); // Allocation pour les colonnes
    }

    // Initialisation et affichage
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            matrix[i][j] = i + j;
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }

    // Libération de la mémoire
    for (int i = 0; i < rows; i++) {
        free(matrix[i]); // Libère chaque ligne
    }
    free(matrix); // Libère les pointeurs des lignes

    return 0;
}

4. Passage de double pointeur à une fonction

Un double pointeur permet de modifier l’adresse d’un pointeur passé en paramètre.

Exemple : Modification d’un pointeur dans une fonction

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

void allouerMemoire(int **ptr) {
    *ptr = malloc(sizeof(int)); // Alloue un entier
    if (*ptr != NULL) {
        **ptr = 42; // Initialise la valeur pointée
    }
}

int main() {
    int *p = NULL;

    allouerMemoire(&p); // Passe l'adresse du pointeur p
    printf("Valeur allouée : %d\n", *p); // Affiche 42

    free(p); // Libère la mémoire
    return 0;
}

5. Manipulation de Tableaux 2D

Un tableau 2D peut être représenté à l’aide de doubles pointeurs.

Exemple : Manipulation simple d’un tableau 2D

#include <stdio.h>

int main() {
    int array[2][3] = {{1, 2, 3}, {4, 5, 6}};
    int *ptr1 = array[0];
    int **ptr2 = &ptr1;

    printf("Accès via double pointeur : %d\n", **ptr2); // Affiche 1
    return 0;
}

6. Double Pointeur et Chaînes de Caractères

Les doubles pointeurs sont souvent utilisés pour manipuler des tableaux de chaînes de caractères.

Exemple : Tableaux de chaînes de caractères

#include <stdio.h>

int main() {
    char *noms[] = {"Alice", "Bob", "Charlie"};
    char **ptr = noms;

    for (int i = 0; i < 3; i++) {
        printf("%s\n", ptr[i]); // Affiche chaque chaîne
    }
    return 0;
}

7. Différences entre Pointeur et Double Pointeur

PointeurDouble Pointeur
Contient l’adresse d’une variable.Contient l’adresse d’un pointeur.
Utilisé pour manipuler directement des variables.Utilisé pour manipuler des pointeurs.
Accès via un astérisque (*).Accès via deux astérisques (**).

8. Précautions avec les Doubles Pointeurs

  1. Initialisation obligatoire : Assurez-vous que les pointeurs (et doubles pointeurs) sont correctement initialisés avant utilisation.
  2. Gestion de mémoire : Pour chaque malloc sur un pointeur, assurez-vous d’appeler free.
  3. Vérification des pointeurs NULL : Avant d’accéder à un double pointeur, vérifiez que les pointeurs sont non nuls.

9. Cas Pratique : Liste Chaînée avec Double Pointeur

Les doubles pointeurs sont utiles pour manipuler une liste chaînée, notamment pour insérer un élément en tête.

Exemple : Insertion en tête

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

typedef struct Node {
    int data;
    struct Node *next;
} Node;

void insererEnTete(Node **head, int valeur) {
    Node *nouveau = malloc(sizeof(Node));
    nouveau->data = valeur;
    nouveau->next = *head;
    *head = nouveau; // Modifie le pointeur head dans la fonction
}

void afficherListe(Node *head) {
    while (head != NULL) {
        printf("%d -> ", head->data);
        head = head->next;
    }
    printf("NULL\n");
}

int main() {
    Node *head = NULL;

    insererEnTete(&head, 3);
    insererEnTete(&head, 5);
    insererEnTete(&head, 7);

    afficherListe(head);

    return 0;
}

10. Résumé des Applications

  1. Allocation dynamique : Créez des structures complexes comme des tableaux 2D.
  2. Passage de pointeur à une fonction : Modifiez directement les pointeurs dans une fonction.
  3. Manipulation de chaînes de caractères : Gérez des tableaux de chaînes de manière dynamique.
  4. Structures dynamiques : Implémentez des structures comme des listes chaînées.

Voici des exemples corrigés et commentés pour illustrer l’utilisation correcte des doubles pointeurs en C. Ces exemples couvrent différents cas pratiques.


Exemple 1 : Allouer et modifier un pointeur avec un double pointeur

Code incorrect :

#include <stdlib.h>
void allouerMemoire(int *ptr) {
    ptr = malloc(sizeof(int)); // Change une copie locale de ptr
    *ptr = 42; // Cela provoque un comportement indéfini si malloc échoue
}

int main() {
    int *p = NULL;
    allouerMemoire(p); // p reste NULL après l'appel
    printf("Valeur : %d\n", *p); // Erreur de segmentation
    return 0;
}

Correction :

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

void allouerMemoire(int **ptr) {
    *ptr = malloc(sizeof(int)); // Alloue de la mémoire à l'adresse du pointeur
    if (*ptr != NULL) {
        **ptr = 42; // Initialise la valeur pointée
    }
}

int main() {
    int *p = NULL;
    allouerMemoire(&p); // Passe l'adresse de p

    if (p != NULL) {
        printf("Valeur : %d\n", *p); // Affiche 42
        free(p); // Libère la mémoire
    } else {
        printf("Allocation échouée\n");
    }

    return 0;
}

Exemple 2 : Allocation dynamique d’un tableau 2D

Code incorrect :

int main() {
    int **tableau = malloc(3 * sizeof(int *)); // Allocation pour les lignes
    for (int i = 0; i < 3; i++) {
        tableau[i] = malloc(4 * sizeof(int)); // Allocation pour les colonnes
    }
    tableau[3][0] = 10; // Dépassement de mémoire, 3 est hors limites
    return 0;
}

Correction :

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

int main() {
    int rows = 3, cols = 4;
    int **tableau = malloc(rows * sizeof(int *)); // Allocation pour les lignes
    if (tableau == NULL) {
        printf("Échec de l'allocation mémoire\n");
        return 1;
    }

    for (int i = 0; i < rows; i++) {
        tableau[i] = malloc(cols * sizeof(int)); // Allocation pour les colonnes
        if (tableau[i] == NULL) {
            printf("Échec de l'allocation mémoire\n");
            return 1;
        }
    }

    // Initialisation et affichage
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            tableau[i][j] = i * cols + j;
            printf("%d ", tableau[i][j]);
        }
        printf("\n");
    }

    // Libération de la mémoire
    for (int i = 0; i < rows; i++) {
        free(tableau[i]); // Libère chaque ligne
    }
    free(tableau); // Libère les pointeurs des lignes

    return 0;
}

Exemple 3 : Modification d’un tableau avec un double pointeur

Code incorrect :

void remplirTableau(int *tab) {
    tab[0] = 10; // Modifie une copie locale du tableau
}

int main() {
    int *tableau = malloc(5 * sizeof(int));
    remplirTableau(tableau); // Le tableau n'est pas initialisé correctement
    printf("%d\n", tableau[0]); // Résultat indéfini
    free(tableau);
    return 0;
}

Correction :

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

void remplirTableau(int **tab, int taille) {
    *tab = malloc(taille * sizeof(int)); // Alloue la mémoire
    if (*tab != NULL) {
        for (int i = 0; i < taille; i++) {
            (*tab)[i] = i * 2; // Initialise le tableau
        }
    }
}

int main() {
    int *tableau = NULL;
    remplirTableau(&tableau, 5); // Passe l'adresse du pointeur

    if (tableau != NULL) {
        for (int i = 0; i < 5; i++) {
            printf("%d ", tableau[i]); // Affiche : 0 2 4 6 8
        }
        printf("\n");
        free(tableau); // Libère la mémoire
    }

    return 0;
}

Exemple 4 : Gestion d’un tableau de chaînes de caractères

Code incorrect :

void remplirNoms(char **noms) {
    noms[0] = "Alice"; // Erreur : mémoire potentiellement non allouée
}

int main() {
    char **noms = NULL;
    remplirNoms(noms); // noms reste NULL
    printf("%s\n", noms[0]); // Comportement indéfini
    return 0;
}

Correction :

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

void remplirNoms(char ***noms, int taille) {
    *noms = malloc(taille * sizeof(char *)); // Alloue pour le tableau de chaînes
    if (*noms == NULL) {
        printf("Échec de l'allocation mémoire\n");
        return;
    }

    (*noms)[0] = strdup("Alice"); // Alloue et copie la chaîne
    (*noms)[1] = strdup("Bob");
    (*noms)[2] = strdup("Charlie");
}

int main() {
    char **noms = NULL;
    remplirNoms(&noms, 3);

    if (noms != NULL) {
        for (int i = 0; i < 3; i++) {
            printf("%s\n", noms[i]); // Affiche les noms
            free(noms[i]); // Libère chaque chaîne
        }
        free(noms); // Libère le tableau de pointeurs
    }

    return 0;
}

Exemple 5 : Manipulation d’une liste chaînée avec double pointeur

Code incorrect :

typedef struct Node {
    int data;
    struct Node *next;
} Node;

void ajouterEnTete(Node *head, int valeur) {
    Node *nouveau = malloc(sizeof(Node));
    nouveau->data = valeur;
    nouveau->next = head;
    head = nouveau; // Modifie uniquement une copie locale
}

int main() {
    Node *head = NULL;
    ajouterEnTete(head, 10); // head reste NULL
    return 0;
}

Correction :

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

typedef struct Node {
    int data;
    struct Node *next;
} Node;

void ajouterEnTete(Node **head, int valeur) {
    Node *nouveau = malloc(sizeof(Node));
    if (nouveau == NULL) {
        printf("Échec de l'allocation mémoire\n");
        return;
    }
    nouveau->data = valeur;
    nouveau->next = *head;
    *head = nouveau; // Modifie le pointeur réel
}

void afficherListe(Node *head) {
    while (head != NULL) {
        printf("%d -> ", head->data);
        head = head->next;
    }
    printf("NULL\n");
}

int main() {
    Node *head = NULL;
    ajouterEnTete(&head, 10);
    ajouterEnTete(&head, 20);
    ajouterEnTete(&head, 30);

    afficherListe(head); // Affiche : 30 -> 20 -> 10 -> NULL

    // Libération de la liste
    Node *temp;
    while (head != NULL) {
        temp = head;
        head = head->next;
        free(temp);
    }

    return 0;
}

Résumé des Correctifs

  1. Passer l’adresse d’un pointeur pour permettre la modification dans une fonction.
  2. Allouer correctement la mémoire pour les doubles pointeurs et leurs éléments.
  3. Toujours vérifier que les allocations sont réussies (!= NULL).
  4. Libérer toute mémoire allouée pour éviter les fuites mémoire.
  5. Utiliser des outils comme valgrind pour vérifier l’utilisation correcte de la mémoire.

Ces exemples corrigés et optimisés montrent comment manipuler efficacement les doubles pointeurs tout en évitant les erreurs courantes en C.

Recommandés

Guide : L'Allocation Dynamique en C
L'allocation dynamique en C permet de...
En savoir plus
Guide complet : strcpy en C
La fonction strcpy en C est...
En savoir plus
Guide : Les Tableaux en C -...
Les tableaux en langage C sont...
En savoir plus
Fiche Pratique : Réussir un QCM en...
Un questionnaire à choix multiple (QCM)...
En savoir plus
Les Pointeurs en Langage C : Astuces...
Les pointeurs sont l'un des aspects...
En savoir plus
Structure d'un Programme en Langage C
Le langage de programmation C, développé...
En savoir plus
AZ

Recent Posts

Classification des Documents : Organiser et Automatiser la Gestion Documentaire

Dans toute organisation moderne — entreprise, association, service administratif ou bureau de projet — la…

14 heures ago

Modèle de Bilan Actif Passif sur Excel : Concevoir un tableau comptable clair et automatisé

Dans la pratique comptable, le bilan constitue l’un des documents les plus fondamentaux pour comprendre…

16 heures ago

Fiche Méthode analyse linéaire + guide complet pour la réussir

L’analyse linéaire impressionne souvent plus qu’elle ne le devrait. Au moment d’aborder l’oral du bac…

1 jour ago

Analyse linéaire au bac français : méthode complète, exemples et conseils pour réussir l’oral

L’analyse linéaire occupe une place centrale à l’oral du bac français. C’est l’exercice qui permet…

1 jour ago

Créer une fiche de suivi en ligne : générateur personnalisable à imprimer

Créer une fiche de suivi claire et adaptée à son activité prend souvent plus de…

1 jour ago

Préparation physique football avec ballon : Fiche Word utile

Comment améliorer sa condition physique tout en travaillant la technique Quand on parle de préparation…

2 jours ago

This website uses cookies.