Un pointeur de pointeur (ou double pointeur) en C est une variable qui contient l’adresse d’un pointeur, lequel pointe lui-même vers une autre variable. Les pointeurs de pointeurs sont utilisés pour gérer des structures complexes, comme des tableaux 2D, pour manipuler des pointeurs dans des fonctions, et pour gérer dynamiquement la mémoire.
Un pointeur de pointeur est déclaré en ajoutant deux astérisques (**
) dans sa définition.
type **nom_du_pointeur;
int **ptr; // ptr est un pointeur vers un pointeur d'entiers
Dans cet exemple, ptr
peut contenir l’adresse d’un autre pointeur qui, lui, pointe vers un entier.
Pour comprendre le fonctionnement, imaginons trois niveaux de mémoire :
#include <stdio.h>
int main() {
int a = 10;
int *p = &a; // p pointe vers a
int **pp = &p; // pp pointe vers p
printf("Valeur de a : %d\n", a);
printf("Adresse de a (contenue dans p) : %p\n", (void *)p);
printf("Adresse de p (contenue dans pp) : %p\n", (void *)pp);
printf("Valeur de a via pp : %d\n", **pp); // Double déréférencement
return 0;
}
Un pointeur de pointeur est souvent utilisé pour modifier un pointeur dans une fonction.
#include <stdio.h>
#include <stdlib.h>
void allouerMemoire(int **ptr) {
*ptr = malloc(sizeof(int)); // Alloue de la mémoire pour un entier
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
}
return 0;
}
Les pointeurs de pointeurs sont utilisés pour créer des tableaux 2D dynamiques.
#include <stdio.h>
#include <stdlib.h>
int main() {
int rows = 3, cols = 4;
int **matrix = malloc(rows * sizeof(int *)); // Alloue un tableau de pointeurs
for (int i = 0; i < rows; i++) {
matrix[i] = malloc(cols * sizeof(int)); // Alloue chaque ligne
}
// Initialisation
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = i + j;
}
}
// Affichage
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
// Libération de la mémoire
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
return 0;
}
Les pointeurs de pointeurs sont utilisés pour gérer des tableaux de chaînes de caractères.
#include <stdio.h>
int main() {
char *noms[] = {"Alice", "Bob", "Charlie"};
char **ptr = noms; // Pointeur vers un tableau de chaînes
for (int i = 0; i < 3; i++) {
printf("%s\n", ptr[i]); // Affiche chaque chaîne
}
return 0;
}
Les pointeurs de pointeurs permettent d’insérer des éléments dans une liste chaînée.
#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) {
nouveau->data = valeur;
nouveau->next = *head;
*head = nouveau;
}
}
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);
// Libération de la mémoire
Node *temp;
while (head != NULL) {
temp = head;
head = head->next;
free(temp);
}
return 0;
}
int **pp = NULL;
malloc
ne retourne pas NULL
.free(pointeur[i]); // Libère chaque ligne free(pointeur); // Libère les pointeurs des lignes
Avantages | Description |
---|---|
Manipulation flexible | Permet de modifier des pointeurs à l’intérieur des fonctions. |
Gestion de structures complexes | Simplifie la gestion des tableaux 2D ou des listes chaînées. |
Optimisation mémoire | Prend en charge des allocations dynamiques pour des besoins variés. |
Les pointeurs de pointeurs en C sont des outils puissants pour manipuler des structures complexes, mais leur utilisation peut présenter des cas particuliers qui nécessitent une attention particulière. Ce guide couvre ces situations, avec des explications et des exemples.
Un pointeur de pointeur peut être NULL, comme n’importe quel autre pointeur. Il est essentiel de vérifier son état avant de l’utiliser pour éviter des comportements indéfinis.
#include <stdio.h>
#include <stdlib.h>
void verifierPointeur(int **pp) {
if (pp == NULL || *pp == NULL) {
printf("Le pointeur ou le contenu du pointeur est NULL\n");
} else {
printf("Valeur : %d\n", **pp);
}
}
int main() {
int *p = NULL;
int **pp = &p;
verifierPointeur(pp); // Affiche : Le pointeur ou le contenu du pointeur est NULL
int x = 42;
p = &x;
verifierPointeur(pp); // Affiche : Valeur : 42
return 0;
}
Un pointeur de pointeur peut devenir dangling (non valide) si la mémoire à laquelle il fait référence est libérée.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = malloc(sizeof(int));
int **pp = &p;
*p = 100;
free(p); // Libère la mémoire pointée par p
printf("Valeur après free : %d\n", **pp); // Comportement indéfini
return 0;
}
Toujours réinitialiser les pointeurs après libération.
free(p);
p = NULL; // Évite le problème
Les tableaux dynamiques impliquent souvent l’utilisation de pointeurs de pointeurs. Cependant, des erreurs de manipulation peuvent provoquer des dépassements de mémoire ou des fuites.
#include <stdio.h>
#include <stdlib.h>
int main() {
int **tableau = malloc(3 * sizeof(int *));
for (int i = 0; i < 3; i++) {
tableau[i] = malloc(4 * sizeof(int)); // Alloue chaque ligne
}
tableau[3][0] = 10; // Erreur : dépassement, tableau a 3 lignes (indices 0 à 2)
// Libération partielle
free(tableau[0]);
free(tableau); // Fuite de mémoire pour tableau[1] et tableau[2]
return 0;
}
Vérifiez toujours les limites et libérez toute la mémoire.
for (int i = 0; i < 3; i++) {
free(tableau[i]);
}
free(tableau);
Passer un pointeur de pointeur à une fonction pour modifier un pointeur peut entraîner des erreurs si les pointeurs ne sont pas correctement initialisés.
#include <stdio.h>
void modifierPointeur(int **pp) {
*pp = malloc(sizeof(int)); // Si pp est NULL, comportement indéfini
**pp = 42;
}
int main() {
int **pp = NULL; // Non initialisé
modifierPointeur(pp); // Erreur : accès à une adresse invalide
return 0;
}
Assurez-vous que le pointeur est initialisé avant de le manipuler.
int main() {
int *p = NULL;
int **pp = &p; // pp pointe vers p
modifierPointeur(pp);
printf("Valeur : %d\n", *p); // Affiche 42
free(p); // Libère la mémoire
return 0;
}
Les tableaux de chaînes de caractères impliquent des pointeurs de pointeurs, et leur gestion peut être complexe.
#include <stdio.h>
#include <stdlib.h>
int main() {
char **noms = malloc(3 * sizeof(char *));
noms[0] = "Alice"; // Correct, mais non alloué dynamiquement
noms[1] = "Bob";
noms[2] = "Charlie";
free(noms[0]); // Erreur : "Alice" n'a pas été alloué avec malloc
free(noms);
return 0;
}
Allouez chaque chaîne dynamiquement.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char **noms = malloc(3 * sizeof(char *));
noms[0] = strdup("Alice");
noms[1] = strdup("Bob");
noms[2] = strdup("Charlie");
for (int i = 0; i < 3; i++) {
printf("%s\n", noms[i]); // Affiche chaque nom
free(noms[i]); // Libère chaque chaîne
}
free(noms); // Libère le tableau de pointeurs
return 0;
}
Un tableau 2D est souvent utilisé comme une matrice en C, mais il peut être manipulé avec des pointeurs de pointeurs, ce qui peut créer de la confusion.
#include <stdio.h>
void afficherMatrice(int **mat, int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", mat[i][j]); // Comportement indéfini si la mémoire n'est pas contiguë
}
printf("\n");
}
}
int main() {
int mat[2][2] = {{1, 2}, {3, 4}};
afficherMatrice((int **)mat, 2, 2); // Conversion incorrecte, les pointeurs ne sont pas compatibles
return 0;
}
Si vous utilisez un tableau statique, transmettez son pointeur.
void afficherMatrice(int mat[2][2], int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", mat[i][j]);
}
printf("\n");
}
}
Si la matrice est allouée dynamiquement, utilisez un double pointeur correctement configuré.
Les erreurs fréquentes avec les pointeurs de pointeurs incluent les fuites de mémoire. Utilisez des outils comme Valgrind pour vérifier.
valgrind --leak-check=full ./programme
Cas | Précaution / Solution |
---|---|
Pointeur NULL | Toujours vérifier avant d’utiliser un double pointeur. |
Dangling Pointer | Réinitialisez les pointeurs à NULL après libération. |
Tableaux dynamiques | Vérifiez les limites et libérez chaque niveau de mémoire. |
Passer un double pointeur | Assurez-vous que les pointeurs sont initialisés correctement. |
Tableaux de chaînes | Allouez dynamiquement les chaînes si vous utilisez free . |
Conversion tableau/pointeur | Faites attention à la compatibilité des pointeurs et tableaux. |
Ces cas particuliers montrent l’importance de gérer les pointeurs de pointeurs avec soin pour éviter les erreurs courantes en C.
Télécharger un Canevas Word et Excel pour présenter un concept ⬇︎ Un concept novateur repose…
La présentation d’un produit commercial est une étape essentielle pour assurer son succès sur le…
Chaque année, les entreprises sont tenues de réaliser une présentation des comptes annuels, un exercice…
Le stage est une étape clé dans le parcours académique et professionnel d’un étudiant. Il…
L’expression "de par" et la tournure "de part" sont souvent confondues, mais elles ont des…
L’expression "pour ce faire" et la tournure "pour se faire" sont souvent confondues en raison…
This website uses cookies.