Les Pointeurs en C : Exercices Corrigés
Les pointeurs sont une caractéristique puissante du langage C, offrant un contrôle de bas niveau sur la mémoire. Ils permettent la manipulation directe des adresses mémoire, ce qui peut être extrêmement utile pour l’optimisation des performances et la gestion dynamique de la mémoire. Cet article présente des exercices pratiques sur les pointeurs en C, avec des solutions détaillées pour aider à comprendre leur utilisation et leur importance.
Exercice 1 : Comprendre les bases des pointeurs
Énoncé :
Écrire un programme en C qui :
- Déclare un entier
a
et un pointeur vers un entierp
. - Assigne l’adresse de
a
àp
. - Modifie la valeur de
a
en utilisant le pointeurp
. - Affiche l’adresse de
a
, la valeur dea
et la valeur pointée parp
.
Solution :
#include <stdio.h>
int main() {
int a = 10;
int *p;
p = &a; // p pointe vers a
// Afficher les valeurs initiales
printf("Adresse de a : %p\n", (void*)&a);
printf("Valeur de a : %d\n", a);
printf("Valeur pointée par p : %d\n", *p);
*p = 20; // Modifier la valeur de a en utilisant p
// Afficher les valeurs après modification
printf("Valeur de a après modification : %d\n", a);
printf("Valeur pointée par p après modification : %d\n", *p);
return 0;
}
Explication :
Cet exercice permet de comprendre les bases des pointeurs : comment déclarer un pointeur, lui assigner une adresse, et utiliser le pointeur pour modifier la valeur de la variable à laquelle il pointe.
Exercice 2 : Manipulation de tableaux avec des pointeurs
Énoncé :
Écrire un programme en C qui :
- Déclare un tableau d’entiers de taille 5.
- Utilise un pointeur pour accéder et modifier les éléments du tableau.
- Affiche les éléments du tableau avant et après modification.
Solution :
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // p pointe vers le premier élément de arr
// Afficher les éléments du tableau avant modification
printf("Tableau avant modification : ");
for(int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// Modifier les éléments du tableau en utilisant le pointeur
for(int i = 0; i < 5; i++) {
*(p + i) = *(p + i) * 2;
}
// Afficher les éléments du tableau après modification
printf("Tableau après modification : ");
for(int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
Explication :
Cet exercice illustre l’utilisation des pointeurs pour manipuler les éléments d’un tableau. En utilisant un pointeur pour accéder et modifier les éléments du tableau, on peut constater la puissance et la flexibilité des pointeurs.
Exercice 3 : Utilisation de pointeurs pour l’allocation dynamique de mémoire
Énoncé :
Écrire un programme en C qui :
- Utilise
malloc
pour allouer dynamiquement un tableau d’entiers de taille spécifiée par l’utilisateur. - Remplit le tableau avec des valeurs données par l’utilisateur.
- Affiche les éléments du tableau.
- Libère la mémoire allouée dynamiquement.
Solution :
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("Entrez la taille du tableau : ");
scanf("%d", &n);
// Allocation dynamique de mémoire
int *arr = (int*)malloc(n * sizeof(int));
if(arr == NULL) {
printf("Échec de l'allocation de mémoire.\n");
return 1;
}
// Remplir le tableau
printf("Entrez %d entiers :\n", n);
for(int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
// Afficher les éléments du tableau
printf("Éléments du tableau : ");
for(int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// Libérer la mémoire allouée
free(arr);
return 0;
}
Explication :
Cet exercice montre comment utiliser les pointeurs pour allouer et gérer dynamiquement la mémoire. malloc
permet de réserver un espace mémoire pendant l’exécution du programme, ce qui est essentiel pour des structures de données dynamiques.
Exercice 4 : Pointeurs et fonctions
Énoncé :
Écrire un programme en C qui :
- Déclare une fonction qui prend un pointeur vers un entier et double la valeur de cet entier.
- Utilise cette fonction pour doubler la valeur d’un entier donné.
Solution :
#include <stdio.h>
// Fonction qui double la valeur d'un entier
void doubleValue(int *p) {
*p = *p * 2;
}
int main() {
int a = 5;
printf("Valeur de a avant : %d\n", a);
doubleValue(&a); // Passer l'adresse de a
printf("Valeur de a après : %d\n", a);
return 0;
}
Explication :
Cet exercice montre comment les pointeurs peuvent être utilisés pour passer des arguments par référence aux fonctions. En passant l’adresse d’une variable à une fonction, la fonction peut modifier directement la valeur de la variable
Exercice 5 : Manipulation de chaînes de caractères avec des pointeurs
Énoncé :
Écrire un programme en C qui :
- Déclare une chaîne de caractères et un pointeur vers un caractère.
- Utilise le pointeur pour inverser la chaîne de caractères.
- Affiche la chaîne de caractères avant et après l’inversion.
Solution :
#include <stdio.h>
#include <string.h>
void reverseString(char *str) {
int length = strlen(str);
char *start = str;
char *end = str + length - 1;
char temp;
while (start < end) {
temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
}
int main() {
char str[] = "Hello, world!";
printf("Chaîne avant inversion : %s\n", str);
reverseString(str);
printf("Chaîne après inversion : %s\n", str);
return 0;
}
Explication :
Cet exercice montre comment utiliser les pointeurs pour manipuler les chaînes de caractères. La fonction reverseString
utilise des pointeurs pour échanger les caractères aux extrémités de la chaîne, progressant vers le centre jusqu’à ce que la chaîne soit complètement inversée.
Exercice 6 : Utilisation de pointeurs pour des matrices
Énoncé :
Écrire un programme en C qui :
- Utilise
malloc
pour allouer dynamiquement une matrice de taillem x n
spécifiée par l’utilisateur. - Remplit la matrice avec des valeurs données par l’utilisateur.
- Affiche les éléments de la matrice.
- Libère la mémoire allouée dynamiquement.
Solution :
#include <stdio.h>
#include <stdlib.h>
int main() {
int m, n;
printf("Entrez le nombre de lignes : ");
scanf("%d", &m);
printf("Entrez le nombre de colonnes : ");
scanf("%d", &n);
// Allocation dynamique de la matrice
int **matrix = (int **)malloc(m * sizeof(int *));
for(int i = 0; i < m; i++) {
matrix[i] = (int *)malloc(n * sizeof(int));
}
// Remplir la matrice
printf("Entrez les éléments de la matrice (%d x %d) :\n", m, n);
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
scanf("%d", &matrix[i][j]);
}
}
// Afficher la matrice
printf("Éléments de la matrice :\n");
for(int i = 0; i < m; i++) {
for(int j = 0; j < n; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
// Libérer la mémoire allouée
for(int i = 0; i < m; i++) {
free(matrix[i]);
}
free(matrix);
return 0;
}
Explication :
Cet exercice introduit la gestion des matrices à l’aide de pointeurs et d’allocation dynamique de mémoire. La matrice est représentée par un tableau de pointeurs, chaque pointeur pointant vers une ligne de la matrice.
Exercice 7 : Manipulation de structures avec des pointeurs
Énoncé :
Écrire un programme en C qui :
- Déclare une structure
Personne
avec des champs pour le nom, l’âge et l’adresse. - Utilise un pointeur pour initialiser et afficher les informations d’une personne.
Solution :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char nom[50];
int age;
char adresse[100];
} Personne;
void afficherPersonne(Personne *p) {
printf("Nom : %s\n", p->nom);
printf("Âge : %d\n", p->age);
printf("Adresse : %s\n", p->adresse);
}
int main() {
Personne *p = (Personne *)malloc(sizeof(Personne));
strcpy(p->nom, "Alice Dupont");
p->age = 30;
strcpy(p->adresse, "123 Rue Principale, Paris");
afficherPersonne(p);
free(p);
return 0;
}
Explication :
Cet exercice montre comment utiliser des pointeurs pour manipuler des structures. En allouant dynamiquement une structure Personne
, on peut initialiser ses champs et les afficher en utilisant un pointeur.
Exercice 8 : Pointeurs et récursivité
Énoncé :
Écrire un programme en C qui :
- Utilise une fonction récursive pour calculer la somme des éléments d’un tableau d’entiers.
- Utilise des pointeurs pour passer le tableau et sa taille à la fonction récursive.
Solution :
#include <stdio.h>
int sommeTableau(int *arr, int taille) {
if (taille == 0) {
return 0;
} else {
return arr[0] + sommeTableau(arr + 1, taille - 1);
}
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
int taille = sizeof(arr) / sizeof(arr[0]);
int somme = sommeTableau(arr, taille);
printf("Somme des éléments du tableau : %d\n", somme);
return 0;
}
Explication :
Cet exercice combine l’utilisation des pointeurs et la récursivité pour résoudre un problème. La fonction sommeTableau
calcule la somme des éléments d’un tableau en utilisant un pointeur pour accéder aux éléments et réduire la taille du tableau de manière récursive.
Exercice 9 : Gestion des pointeurs de fonctions
Énoncé :
Écrire un programme en C qui :
- Déclare deux fonctions,
addition
etmultiplication
, qui prennent deux entiers en paramètres et renvoient leur somme et leur produit respectivement. - Déclare un pointeur de fonction.
- Utilise le pointeur de fonction pour appeler dynamiquement
addition
etmultiplication
en fonction de l’entrée de l’utilisateur.
Solution :
#include <stdio.h>
int addition(int a, int b) {
return a + b;
}
int multiplication(int a, int b) {
return a * b;
}
int main() {
int (*operation)(int, int); // Déclaration du pointeur de fonction
int choix, x, y;
printf("Choisissez l'opération : 1 pour addition, 2 pour multiplication : ");
scanf("%d", &choix);
printf("Entrez deux entiers : ");
scanf("%d %d", &x, &y);
if (choix == 1) {
operation = addition;
} else if (choix == 2) {
operation = multiplication;
} else {
printf("Choix invalide.\n");
return 1;
}
int resultat = operation(x, y);
printf("Le résultat est : %d\n", resultat);
return 0;
}
Explication :
Cet exercice montre comment utiliser des pointeurs de fonctions pour appeler dynamiquement des fonctions en fonction de l’entrée de l’utilisateur. Cela permet de rendre le code plus flexible et modulaire.
Exercice 10 : Manipulation de listes chaînées avec des pointeurs
Énoncé :
Écrire un programme en C qui :
- Déclare une structure pour représenter un nœud dans une liste chaînée (contenant un entier et un pointeur vers le nœud suivant).
- Implémente des fonctions pour ajouter un nœud en tête, ajouter un nœud en queue, et afficher tous les nœuds de la liste.
- Utilise ces fonctions pour créer une liste chaînée et afficher son contenu.
Solution :
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *next;
} Node;
void ajouterEnTete(Node **head, int newData) {
Node *newNode = (Node *)malloc(sizeof(Node));
newNode->data = newData;
newNode->next = *head;
*head = newNode;
}
void ajouterEnQueue(Node **head, int newData) {
Node *newNode = (Node *)malloc(sizeof(Node));
newNode->data = newData;
newNode->next = NULL;
if (*head == NULL) {
*head = newNode;
return;
}
Node *temp = *head;
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = newNode;
}
void afficherListe(Node *node) {
while (node != NULL) {
printf("%d -> ", node->data);
node = node->next;
}
printf("NULL\n");
}
int main() {
Node *head = NULL;
ajouterEnTete(&head, 10);
ajouterEnTete(&head, 20);
ajouterEnQueue(&head, 30);
ajouterEnQueue(&head, 40);
printf("Contenu de la liste : ");
afficherListe(head);
return 0;
}
Explication :
Cet exercice introduit la manipulation de listes chaînées en utilisant des pointeurs. Les fonctions permettent d’ajouter des nœuds en tête ou en queue de la liste et d’afficher son contenu, illustrant ainsi la gestion dynamique des structures de données.
Exercice 11 : Pointeurs et tableaux multidimensionnels
Énoncé :
Écrire un programme en C qui :
- Déclare et initialise une matrice 3×3.
- Utilise des pointeurs pour transposer la matrice.
- Affiche la matrice originale et la matrice transposée.
Solution :
#include <stdio.h>
void afficherMatrice(int mat[3][3], int taille) {
for (int i = 0; i < taille; i++) {
for (int j = 0; j < taille; j++) {
printf("%d ", mat[i][j]);
}
printf("\n");
}
}
void transpose(int (*mat)[3], int taille) {
for (int i = 0; i < taille; i++) {
for (int j = i; j < taille; j++) {
int temp = mat[i][j];
mat[i][j] = mat[j][i];
mat[j][i] = temp;
}
}
}
int main() {
int mat[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
printf("Matrice originale :\n");
afficherMatrice(mat, 3);
transpose(mat, 3);
printf("Matrice transposée :\n");
afficherMatrice(mat, 3);
return 0;
}
Explication :
Cet exercice démontre comment utiliser des pointeurs pour manipuler des tableaux multidimensionnels. La fonction transpose
utilise des pointeurs pour échanger les éléments de la matrice afin de la transposer.
Exercice 12 : Gestion de la mémoire avec calloc
et realloc
Énoncé :
Écrire un programme en C qui :
- Utilise
calloc
pour allouer dynamiquement un tableau d’entiers initialisé à zéro. - Utilise
realloc
pour redimensionner le tableau. - Remplit le tableau avec des valeurs données par l’utilisateur et affiche les éléments du tableau après chaque redimensionnement.
- Libère la mémoire allouée dynamiquement.
Solution :
#include <stdio.h>
#include <stdlib.h>
void afficherTableau(int *arr, int taille) {
for (int i = 0; i < taille; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int tailleInitiale, nouvelleTaille;
printf("Entrez la taille initiale du tableau : ");
scanf("%d", &tailleInitiale);
int *arr = (int *)calloc(tailleInitiale, sizeof(int));
if (arr == NULL) {
printf("Échec de l'allocation de mémoire.\n");
return 1;
}
printf("Entrez les valeurs pour le tableau initial :\n");
for (int i = 0; i < tailleInitiale; i++) {
scanf("%d", &arr[i]);
}
printf("Tableau initial : ");
afficherTableau(arr, tailleInitiale);
printf("Entrez la nouvelle taille du tableau : ");
scanf("%d", &nouvelleTaille);
arr = (int *)realloc(arr, nouvelleTaille * sizeof(int));
if (arr == NULL) {
printf("Échec de l'allocation de mémoire.\n");
return 1;
}
printf("Entrez les valeurs supplémentaires pour le tableau :\n");
for (int i = tailleInitiale; i < nouvelleTaille; i++) {
scanf("%d", &arr[i]);
}
printf("Tableau redimensionné : ");
afficherTableau(arr, nouvelleTaille);
free(arr);
return 0;
}
Explication :
Cet exercice montre comment utiliser calloc
pour allouer dynamiquement de la mémoire initialisée à zéro, et realloc
pour redimensionner dynamiquement cette mémoire. Il illustre également l’importance de libérer la mémoire allouée pour éviter les fuites de mémoire.