0% ont trouvé ce document utile (0 vote)
4 vues13 pages

TP CPP

Ce document contient une série d'exercices corrigés sur les références et la gestion de la mémoire en C++. Il aborde des concepts tels que les références, les pointeurs, l'allocation dynamique, la détection d'erreurs mémoire et l'utilisation de smart pointers. Chaque exercice est accompagné d'une solution détaillée pour aider à la compréhension des notions présentées.

Transféré par

stephan tincho
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
4 vues13 pages

TP CPP

Ce document contient une série d'exercices corrigés sur les références et la gestion de la mémoire en C++. Il aborde des concepts tels que les références, les pointeurs, l'allocation dynamique, la détection d'erreurs mémoire et l'utilisation de smart pointers. Chaque exercice est accompagné d'une solution détaillée pour aider à la compréhension des notions présentées.

Transféré par

stephan tincho
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Exercices corrigés – Références et gestion mémoire en

C++
Stephan Tincho

Table des matières


1 Exercices sur les références 3

2 Exercices sur le passage de paramètres 5

3 Exercices sur l’allocation dynamique 6

4 Exercices de détection et correction d’erreurs mémoire 8

5 Exercice de synthèse : mini-vecteur dynamique 11

6 Exercices bonus – Smart pointers 13

1
Introduction
Ces exercices sont conçus pour vous aider à maîtriser les notions fondamentales de la
séance 2 :
— Les références (alias, règles, références const).
— Les pointeurs (rappels, différences avec les références).
— L’allocation dynamique avec new et delete (variables simples et tableaux).
— Le passage de paramètres (valeur, référence, référence constante).
— La détection et la correction des erreurs mémoire classiques.
— Une introduction au concept RAII et aux smart pointers.
Tous les exercices sont accompagnés d’un corrigé détaillé. Essayez de les résoudre par
vous-même avant de consulter les solutions.

2
1 Exercices sur les références
Exercice
1 – Comportement d’une référence Analysez le code suivant et prédisez l’affichage
avant de l’exécuter.
1 # include < iostream >
2 using namespace std ;
3

4 int main () {
5 int a = 10;
6 int b = 20;
7 int & ref = a ;
8

9 ref = 50;
10 cout << " a = " << a << " , b = " << b << " , ref = " << ref <<
endl ;
11

12 ref = b ;
13 cout << " a = " << a << " , b = " << b << " , ref = " << ref <<
endl ;
14

15 b = 99;
16 cout << " a = " << a << " , b = " << b << " , ref = " << ref <<
endl ;
17

18 return 0;
19 }

Questions :
1. Quelles sont les valeurs affichées ?
2. Après ref = b, ref devient-il un alias de b ? Justifiez.
3. Pourquoi la modification de b à 99 n’affecte-t-elle pas a ?

Corrigé
Affichage produit :
a=50, b=20, ref=50
a=20, b=20, ref=20
a=20, b=99, ref=20

Explications :
1. ref est un alias de a dès sa déclaration. ref = 50 modifie donc a qui devient
50.
2. ref = b ne change pas la liaison de ref : ref reste un alias de a. Cette
instruction copie la valeur de b (20) dans a (via ref). ref ne devient pas un
alias de b.
3. b est une variable indépendante. Modifier b n’a aucun effet sur a ni sur ref.

3
Exercice
2 – Référence constante Écrivez une fonction afficherMajuscule qui prend une
chaîne de caractères (std::string) par référence constante et l’affiche entièrement
en majuscules (utilisez toupper de <cctype>). Expliquez pourquoi la référence
constante est appropriée.

Corrigé

1 # include < iostream >


2 # include < string >
3 # include < cctype >
4

5 void afficherMajuscule ( const std :: string & texte ) {


6 for ( char c : texte ) {
7 std :: cout << static_cast < char >( std :: toupper ( c ) ) ;
8 }
9 std :: cout << std :: endl ;
10 }
11

12 int main () {
13 std :: string message = " Bonjour , monde ! " ;
14 afficherMajuscule ( message ) ; // Affiche " BONJOUR , MONDE !"
15 return 0;
16 }

La référence constante (const std::string&) évite une copie coûteuse de la chaîne


et garantit que la fonction ne peut pas modifier l’original.

4
2 Exercices sur le passage de paramètres
Exercice
3 – Fonction d’incrémentation Écrivez une fonction incrementer qui prend un
entier par référence et un pas (entier) et incrémente l’entier du pas donné. Testez-la
dans un main.

Corrigé

1 # include < iostream >


2 using namespace std ;
3

4 void incrementer ( int & valeur , int pas ) {


5 valeur += pas ;
6 }
7

8 int main () {
9 int compteur = 0;
10 incrementer ( compteur , 5) ;
11 cout << compteur << endl ; // Affiche 5
12 incrementer ( compteur , 3) ;
13 cout << compteur << endl ; // Affiche 8
14 return 0;
15 }

Exercice
4 – Échange de deux valeurs (swap) Écrivez une fonction swap qui échange le contenu
de deux entiers en utilisant des références. Testez-la.

Corrigé

1 # include < iostream >


2 using namespace std ;
3

4 void swap ( int & x , int & y ) {


5 int temp = x ;
6 x = y;
7 y = temp ;
8 }
9

10 int main () {
11 int a = 10 , b = 20;
12 swap (a , b ) ;
13 cout << " a = " << a << " , b = " << b << endl ; // a =20 , b =10
14 return 0;
15 }

5
3 Exercices sur l’allocation dynamique
Exercice
5 – Tableau de carrés parfaits Écrivez un programme qui :
1. Demande un entier n (entre 1 et 10).
2. Alloue dynamiquement un tableau de n entiers.
3. Remplit le tableau avec les n premiers carrés parfaits : 12 , 22 , 32 , . . . .
4. Affiche le tableau.
5. Libère correctement la mémoire.

Corrigé

1 # include < iostream >


2 using namespace std ;
3

4 int main () {
5 int n ;
6 cout << " n (1 -10) : " ;
7 cin >> n ;
8 if ( n < 1 || n > 10) {
9 cerr << " Valeur invalide " << endl ;
10 return 1;
11 }
12

13 int * carres = new int [ n ]; // Allocation


14

15 for ( int i = 0; i < n ; ++ i )


16 carres [ i ] = ( i +1) * ( i +1) ;
17

18 cout << " C a r r s parfaits : " ;


19 for ( int i = 0; i < n ; ++ i )
20 cout << carres [ i ] << " " ;
21 cout << endl ;
22

23 delete [] carres ; // L i b r a t i o n
24 carres = nullptr ;
25

26 return 0;
27 }

Exercice
6 – Allocation d’une chaîne dynamique Demandez à l’utilisateur de saisir une phrase,
allouez dynamiquement un tableau de caractères de la taille exacte nécessaire,
copiez-y la phrase et affichez-la. N’oubliez pas le caractère nul final. (Indice : utili-
sez strlen et strcpy ou mieux, std::string ; mais ici l’objectif est de manipuler
char*).

6
Corrigé

1 # include < iostream >


2 # include < cstring >
3 using namespace std ;
4

5 int main () {
6 char buffer [256];
7 cout << " Saisissez une phrase : " ;
8 cin . getline ( buffer , 256) ;
9

10 int longueur = strlen ( buffer ) + 1; // +1 pour ’\0 ’


11 char * phrase = new char [ longueur ];
12 strcpy ( phrase , buffer ) ;
13

14 cout << " Phrase stockee : " << phrase << endl ;
15

16 delete [] phrase ;
17 return 0;
18 }

7
4 Exercices de détection et correction d’erreurs mé-
moire
Exercice
7 – Code à corriger (4 erreurs) Le code suivant contient plusieurs erreurs de gestion
mémoire. Identifiez-les et proposez une correction.
1 # include < iostream >
2 using namespace std ;
3

4 void traitement ( int n ) {


5 int * tab ;
6 tab = new int [ n ];
7 for ( int i = 0; i < n ; i ++)
8 tab [ i ] = i * 10;
9 if ( n > 5) {
10 cout << " Trop grand ! " << endl ;
11 return ;
12 }
13 for ( int i = 0; i < n ; i ++)
14 cout << tab [ i ] << " " ;
15 cout << endl ;
16 delete tab ;
17 }
18

19 int main () {
20 int * p = new int (10) ;
21 cout << * p << endl ;
22 delete p ;
23 cout << * p << endl ;
24 return 0;
25 }

Corrigé
Erreurs identifiées :
1. int* tab; non initialisé (pas une erreur fatale ici car il est immédiatement
affecté, mais bonne pratique : le mettre à nullptr).
2. Dans le bloc if (n > 5), on return sans libérer le tableau → fuite mémoire.
3. delete tab; alors que le tableau a été alloué avec new[] → comportement
indéfini. Il faut delete[] tab;.
4. Dans main, après delete p, on accède à *p → use-after-free.

Code corrigé :
1 # include < iostream >
2 using namespace std ;
3

4 void traitement ( int n ) {

8
5 int * tab = nullptr ;
6 tab = new int [ n ];
7 for ( int i = 0; i < n ; i ++)
8 tab [ i ] = i * 10;
9 if ( n > 5) {
10 cout << " Trop grand ! " << endl ;
11 delete [] tab ; // L i b r a t i o n avant de quitter
12 return ;
13 }
14 for ( int i = 0; i < n ; i ++)
15 cout << tab [ i ] << " " ;
16 cout << endl ;
17 delete [] tab ;
18 tab = nullptr ;
19 }
20

21 int main () {
22 int * p = new int (10) ;
23 cout << * p << endl ;
24 delete p ;
25 p = nullptr ;
26 // cout << * p << endl ; // S u p p r i m ou d p l a c avant
delete
27 return 0;
28 }

Exercice
8 – Fuite mémoire et RAII Le code suivant provoque une fuite mémoire si une
exception est levée. Expliquez pourquoi et réécrivez-le en utilisant un unique_ptr
pour éviter la fuite.
1 void risquee () {
2 int * donnees = new int [1000];
3 // ... traitement qui peut lever une exception ...
4 delete [] donnees ;
5 }

Corrigé
Si une exception se produit entre new et delete[], la libération n’est jamais ef-
fectuée. Avec unique_ptr (ou vector), la mémoire est automatiquement libérée
lorsque l’objet sort de la portée.
1 # include < memory >
2 void risquee () {
3 std :: unique_ptr < int [] > donnees =
std :: make_unique < int [] >(1000) ;
4 // ... traitement , m m e avec exception , la m m o i r e sera

9
lib r e
5 }

Ou encore mieux, utiliser std::vector<int> donnees(1000);.

10
5 Exercice de synthèse : mini-vecteur dynamique
Exercice
9 – Classe VecteurDyn avec RAII Implémentez une classe VecteurDyn qui gère un
tableau d’entiers alloué dynamiquement. Elle devra respecter le principe RAII :
allocation dans le constructeur, libération dans le destructeur. Fournissez les mé-
thodes :
— VecteurDyn(int n) : constructeur allouant n entiers (initialisés à 0).
— VecteurDyn() : destructeur libérant la mémoire.
— void set(int index, int valeur) et int get(int index) const.
— int getTaille() const.
— void afficher() const.
Testez la classe dans un main.

Corrigé

1 # include < iostream >


2 using namespace std ;
3

4 class VecteurDyn {
5 private :
6 int * donnees ;
7 int taille ;
8 public :
9 VecteurDyn ( int n ) : taille ( n ) {
10 donnees = new int [ n ];
11 for ( int i = 0; i < n ; ++ i )
12 donnees [ i ] = 0;
13 }
14

15 ~ VecteurDyn () {
16 delete [] donnees ;
17 donnees = nullptr ;
18 }
19

20 void set ( int index , int valeur ) {


21 if ( index >= 0 && index < taille )
22 donnees [ index ] = valeur ;
23 }
24

25 int get ( int index ) const {


26 if ( index >= 0 && index < taille )
27 return donnees [ index ];
28 return -1; // Valeur sentinelle
29 }
30

31 int getTaille () const { return taille ; }

11
32

33 void afficher () const {


34 for ( int i = 0; i < taille ; ++ i )
35 cout << donnees [ i ] << " " ;
36 cout << endl ;
37 }
38 };
39

40 int main () {
41 VecteurDyn v (5) ;
42 for ( int i = 0; i < 5; ++ i )
43 v . set (i , ( i +1) *10) ;
44 v . afficher () ; // 10 20 30 40 50
45 cout << " Element 2 : " << v . get (2) << endl ; // 30
46 return 0; // Le destructeur l i b r e automatiquement
47 }

12
6 Exercices bonus – Smart pointers
Exercice
10 – Utilisation de unique_ptr Réécrivez l’exercice 5 (tableau de carrés parfaits)
en utilisant std::unique_ptr<int[]> au lieu de new[]/delete[].

Corrigé

1 # include < iostream >


2 # include < memory >
3 using namespace std ;
4

5 int main () {
6 int n ;
7 cout << " n (1 -10) : " ;
8 cin >> n ;
9 if ( n < 1 || n > 10) return 1;
10

11 unique_ptr < int [] > carres = make_unique < int [] >( n ) ;


12 for ( int i = 0; i < n ; ++ i )
13 carres [ i ] = ( i +1) *( i +1) ;
14

15 cout << " C a r r s : " ;


16 for ( int i = 0; i < n ; ++ i )
17 cout << carres [ i ] << " " ;
18 cout << endl ;
19 // Pas de delete explicite
20 return 0;
21 }

Synthèse des bonnes pratiques


— Toujours initialiser les pointeurs (nullptr).
— Pour chaque new doit correspondre un delete, pour chaque new[] un delete[].
— Mettre les pointeurs à nullptr après delete.
— Préférer les références aux pointeurs quand la valeur nulle n’est pas nécessaire.
— Pour les paramètres volumineux non modifiés, utiliser const T&.
— Utiliser les smart pointers (unique_ptr, shared_ptr) ou les conteneurs de la STL
(comme vector) plutôt que la gestion manuelle.

13

Vous aimerez peut-être aussi