Tableau de Pointeurs en C

Bonjour à tous, futurs maîtres de la programmation ! Aujourd'hui, nous allons entreprendre un voyage passionnant dans le monde de la programmation en C, en explorant plus particulièrement le concept fascinant de Tableau de Pointeurs. Ne vous inquiétez pas si cela vous paraît intimidant au début – je vous promets que à la fin de ce tutoriel, vous manipulerez cette arme puissante avec aisance !

C - Array of Pointers

Qu'est-ce qu'un Tableau de Pointeurs ?

Avant de plonger dans les profondeurs, commençons par les bases. Imaginez que vous avez un tas de baguettes magiques (pointeurs), chacune capable de invoquer des sorts différents (données). Et que diriez-vous si vous pouviez organiser ces baguettes en une rangée ordonnée (tableau) ? C'est essentiellement ce qu'est un tableau de pointeurs – une collection d'adresses mémoire, toutes alignées et prêtes à l'action !

En termes de programmation en C, un tableau de pointeurs est simplement un tableau où chaque élément est un pointeur. Ces pointeurs peuvent pointer vers différents types de données comme des entiers, des caractères, ou même des structures complexes.

Créer un Tableau de Pointeurs

Commençons par créer notre premier tableau de pointeurs. La syntaxe est assez similaire à celle de la création d'un tableau ordinaire, mais avec une petite torsion :

data_type *nom_tableau[taille];

Ici, data_type est le type de données vers lesquelles les pointeurs pointeront, nom_tableau est le nom que vous souhaitez donner à votre tableau, et taille est le nombre de pointeurs que vous souhaitez dans votre tableau.

Voyons cela en action avec un exemple simple :

#include <stdio.h>

int main() {
int *pointeurs_entiers[5];  // Déclarer un tableau de 5 pointeurs entiers

int a = 10, b = 20, c = 30, d = 40, e = 50;

// Assigner des adresses aux pointeurs
pointeurs_entiers[0] = &a;
pointeurs_entiers[1] = &b;
pointeurs_entiers[2] = &c;
pointeurs_entiers[3] = &d;
pointeurs_entiers[4] = &e;

// Afficher les valeurs pointées par chaque pointeur
for(int i = 0; i < 5; i++) {
printf("Valeur à pointeurs_entiers[%d] = %d\n", i, *pointeurs_entiers[i]);
}

return 0;
}

Dans cet exemple, nous avons créé un tableau de 5 pointeurs entiers. Nous assignons ensuite les adresses de cinq variables entières à ces pointeurs. Enfin, nous affichons les valeurs vers lesquelles chaque pointeur pointe.

Lorsque vous exécutez ce code, vous verrez :

Valeur à pointeurs_entiers[0] = 10
Valeur à pointeurs_entiers[1] = 20
Valeur à pointeurs_entiers[2] = 30
Valeur à pointeurs_entiers[3] = 40
Valeur à pointeurs_entiers[4] = 50

N'est-ce pas magique ? Nous avons juste créé notre premier tableau de pointeurs !

Un Tableau de Pointeurs vers des Entiers

Maintenant que nous avons mis les pieds dans l'eau, plongeons un peu plus profondément dans les tableaux de pointeurs vers des entiers. C'est particulièrement utile lorsque vous souhaitez travailler avec plusieurs tableaux ou lorsque vous avez besoin de trier un tableau sans déplacer les données.

Voici un exemple qui montre comment nous pouvons utiliser un tableau de pointeurs vers des entiers pour trier des nombres sans déplacer les valeurs actuelles :

#include <stdio.h>

void echanger(int **a, int **b) {
int *temp = *a;
*a = *b;
*b = temp;
}

int main() {
int nombres[] = {50, 30, 20, 10, 40};
int *ptr[5];

// Initialiser les pointeurs
for(int i = 0; i < 5; i++) {
ptr[i] = &nombres[i];
}

// Trier les pointeurs en fonction des valeurs qu'ils pointent
for(int i = 0; i < 5; i++) {
for(int j = i + 1; j < 5; j++) {
if(*ptr[i] > *ptr[j]) {
echanger(&ptr[i], &ptr[j]);
}
}
}

// Afficher les valeurs triées
printf("Valeurs triées : ");
for(int i = 0; i < 5; i++) {
printf("%d ", *ptr[i]);
}

// Le tableau original reste inchangé
printf("\nTableau original : ");
for(int i = 0; i < 5; i++) {
printf("%d ", nombres[i]);
}

return 0;
}

Ce code montre un usage puissant d'un tableau de pointeurs. Nous trions les pointeurs en fonction des valeurs qu'ils pointent, triant ainsi les données sans déplacer les valeurs originales. C'est comme réorganiser les baguettes magiques sans troubler les sorts qu'elles invoquent !

Un Tableau de Pointeurs vers des Caractères

Ensuite, explorons les tableaux de pointeurs vers des caractères. C'est extrêmement utile lorsque vous travaillez avec des chaînes de caractères en C. En fait, lorsque vous déclarez un tableau de chaînes en C, ce que vous créez réellement est un tableau de pointeurs vers des caractères !

Voici un exemple pour illustrer cela :

#include <stdio.h>

int main() {
char *fruits[] = {
"Pomme",
"Banane",
"Cerise",
"Datte",
"Baie de sureau"
};

int nb_fruits = sizeof(fruits) / sizeof(fruits[0]);

printf("Notre panier de fruits contient :\n");
for(int i = 0; i < nb_fruits; i++) {
printf("%s\n", fruits[i]);
}

// Essayons de changer un fruit
fruits[1] = "Myrtille";

printf("\nAprès un peu de magie, notre panier de fruits contient maintenant :\n");
for(int i = 0; i < nb_fruits; i++) {
printf("%s\n", fruits[i]);
}

return 0;
}

Dans cet exemple de fruits, nous avons créé un tableau de pointeurs vers des caractères (chaînes). Chaque élément du tableau fruits est un pointeur vers le premier caractère de chaque nom de fruit.

La magie ici est que nous pouvons facilement changer des chaînes entières en changeant simplement le pointeur, comme nous l'avons fait avec "Banane" en "Myrtille". C'est comme échanger des livres de sorts complets avec un mouvement de notre baguette magique !

Un Tableau de Pointeurs vers des Structures

Enfin, explorons le dernier et sans doute le plus complexe (et le plus passionnant) usage des tableaux de pointeurs – pointer vers des structures. C'est extrêmement puissant lorsque vous traitez avec des types de données complexes et que vous souhaitez la flexibilité pour les manipuler efficacement.

Voici un exemple qui montre ce concept :

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

struct Sorcier {
char nom[50];
char sort[50];
int pouvoir;
};

int main() {
struct Sorcier *pointeurs_sorciers[3];

// Créer et initialiser les sorciers
for(int i = 0; i < 3; i++) {
pointeurs_sorciers[i] = (struct Sorcier*)malloc(sizeof(struct Sorcier));

printf("Entrer le nom du sorcier : ");
scanf("%s", pointeurs_sorciers[i]->nom);

printf("Entrer le sort préféré du sorcier : ");
scanf("%s", pointeurs_sorciers[i]->sort);

printf("Entrer le niveau de pouvoir du sorcier : ");
scanf("%d", &pointeurs_sorciers[i]->pouvoir);

printf("\n");
}

// Afficher les informations des sorciers
printf("Nos puissants sorciers :\n");
for(int i = 0; i < 3; i++) {
printf("Sorcier : %s, Sort Préféré : %s, Niveau de Pouvoir : %d\n",
pointeurs_sorciers[i]->nom,
pointeurs_sorciers[i]->sort,
pointeurs_sorciers[i]->pouvoir);
}

// N'oublions pas de libérer la mémoire allouée !
for(int i = 0; i < 3; i++) {
free(pointeurs_sorciers[i]);
}

return 0;
}

Dans cet exemple magique, nous avons créé un tableau de pointeurs vers des structures Sorcier. Cela nous permet deallouer dynamiquement de la mémoire pour chaque sorcier et d'accéder à leurs propriétés en utilisant l'opérateur de flèche (->).

Cette approche nous donne la flexibilité de créer et de manipuler des structures de données complexes avec aisance. C'est comme avoir un grimoire qui peut ajouter dynamiquement de nouveaux sorts au fur et à mesure que nous les apprenons !

Et voilà, jeunes programmeurs ! Nous avons parcouru le royaume des tableaux de pointeurs en C, des concepts de base aux concepts plus avancés. souvenez-vous, comme toute magie puissante, les tableaux de pointeurs nécessitent de la pratique pour être maîtrisés. Alors, n'hésitez pas à expérimenter et à créer vos propres programmes magiques !

Voici un tableau de réference rapide des méthodes que nous avons couvertes :

Méthode Description
data_type *nom_tableau[taille]; Déclarer un tableau de pointeurs
nom_tableau[index] = &variable; Assigner une adresse à un pointeur dans le tableau
*nom_tableau[index] Accéder à la valeur pointée par un pointeur dans le tableau
nom_tableau[index]->membre Accéder à un membre d'une structure pointée par un pointeur dans le tableau

Continuez à pratiquer, soyez curieux, et bientôt vous serez invoquant des programmes complexes avec la même aisance qu'un sorcier chevronné. Bon codage !

Credits: Image by storyset