INFO-F105 — Langages de programmation
TP 3 : Modes de stockage et portée en C++
Hakim Boulahya Arnaud Leponce Robin Petit
Renaud Vilain
Département d’Informatique
Faculté des Sciences
Université Libre de Bruxelles
Année académique 2022-2023
Table des matières
1. Théorie
2. Exercices
Table des matières
1. Théorie
2. Exercices
Portée (ou scope)
▶ Lorsqu’une variable est déclarée, elle n’est pas accessible
dans l’entièreté du code.
▶ Il faudrait sinon un nom unique pour chaque variable
créée dans le programme.
▶ Il faut une notion définissant dans quelle portion du code
une variable est accessible.
▶ Différents langages ont des règles différentes.
INFO-F105 1/21
Portée en C++ vs Python
▶ En C++, une variable existe dans un bloc (pensez en
termes d’accolades).
▶ En Python, une variable existe entre sa définition et la fin
de la fonction dans laquelle est définie.
▶ La portée d’une variable est étroitement liée à sa durée de
vie mais à quelques exceptions près.
INFO-F105 2/21
Exemple permissif en Python
Code ok en Python :
1 def is_odd ( n ) :
2 if n % 2 == 0:
3 ret = False
4 else :
5 ret = True
6 return ret
Erreur à la compilation :
1 bool is_odd ( int n ) {
2 if ( n % 2 == 0) {
3 bool ret = false ;
4 } else {
5 bool ret = true ;
6 }
7 return ret ;
8 }
Le return ret; (l. 5) demande une variable ret qui n’existe
pas !
INFO-F105 3/21
Bonne solution
Il faut déclarer ret dans le scope de la fonction en entier.
1 bool is_odd ( int n ) {
2 bool ret ;
3 if ( n % 2 == 0) {
4 ret = false ;
5 } else {
6 ret = true ;
7 }
8 return ret ;
9 }
INFO-F105 4/21
Mais attention au shadowing
1 bool is_odd ( int n ) {
2 bool ret ;
3 if ( n % 2 == 0) {
4 bool ret = false ;
5 } else {
6 bool ret = true ;
7 }
8 return ret ;
9 }
est en réalité équivalent à :
1 bool is_odd ( int n ) {
2 bool ret ;
3 if ( n % 2 == 0) {
4 bool ret2 = false ;
5 } else {
6 bool ret2 = true ;
7 }
8 return ret ;
9 }
INFO-F105 5/21
Shadowing
▶ En C++, rien n’empêche l’existence de plusieurs variables
du même nom (et potentiellement de types différents)
d’être déclarés dans des scopes imbriqués.
▶ Attention : seule la variable de ce nom déclarée dans le
bloc parent le plus bas est accessible.
1 int main () {
2 int x = 6; // x1
3 {
4 int x = 7; // x2
5 {
6 x = 0; // modifie x2 mais pas x1
7 }
8 }
9 return 0;
10 }
INFO-F105 6/21
Durée de vie
▶ La durée de vie précise la portion du code dans laquelle la
variable existe.
▶ La portée d’une variable est en particulier bornée par sa
durée de vie.
▶ La majorité du temps ces deux notions coïncident mais il
faut parler des variables statiques. . .
INFO-F105 7/21
Qualificateur static
Le mot-clef static permet de définir une variable avec une
portée locale mais une durée de vie globale.
1 int fonction () {
2 static int compteur = 0;
3 compteur ++;
4 return compteur ;
5 }
Appeler plusieurs fois fonction() renverra d’abord 1, puis 2,
puis 3, etc.
Attention : une variable statique n’est initialisée qu’une unique
fois !
INFO-F105 8/21
Qualificateur extern
Le mot-clef extern permet de préciser qu’on va utiliser une
variable qui est définie dans une autre unité de compilation
(disons un autre fichier).
1 // declarations . cpp
2 int x = 105;
1 // main . cpp
2 # include < iostream >
3 using namespace std ;
4
5 int main () {
6 extern int x ;
7 cout << x << endl ;
8 return 0;
9 }
g++ -c declarations.cpp -o declarations.o
g++ main.c declarations.o -o example
INFO-F105 9/21
Variable globale statique
▶ static permet de créer une variable globale dont le scope
est local.
▶ Que donne une variable globale statique ?
▶ extern nous montre que la portée d’une variable globale
n’est pas limitée.
▶ static permet de limiter la portée d’une variable globale
au fichier actuel.
INFO-F105 10/21
extern + static
Le code suivant plantera à la compilation (plus précisément
lors de l’édition des liens) :
1 // declarations . cpp
2 static int x = 0;
1 // main . cpp
2 int main () {
3 extern int x ;
4 return 0;
5 }
Remarque : static permet également de limiter la portée
d’une fonction au fichier actuel. Nous en reparlerons quand
nous parlerons des fonctions !
INFO-F105 11/21
Table des matières
1. Théorie
2. Exercices
Exercice 18
Voici un code en C++ qui comporte trois fonctions myDiv, myMod
et la fonction main. myDiv et myMod prennent 2 paramètres et
renvoient le résultat de la division entière et le reste de celle-ci
respectivement.
1 # include < iostream >
2
3 using n a m e s p a c e std ;
4
5 int myDiv ( int x , int y ) {
6 return x / y ;
7 }
8
9 int myMod ( int x , int y ) {
10 return x % y ;
11 }
12
13 int main () {
14 int a , b , c ;
15 cout << " Entrez une valeur pour le dividende ’a ’: " << endl ;
16 cin >> a ;
17 cout << " Entrez une valeur pour le dividende ’b ’: " << endl ;
18 cin >> b ;
19 cout << " Entrez une valeur pour le diviseur ’c ’: " << endl ;
20 cin >> c ;
21 cout << " Le resultat est : " << myDiv (a , myMod (b , c ) ) << endl ;
22 return 0;
23 }
INFO-F105 12/21
Exercice 18
▶ Testez ce programme avec les nombres suivants :
▶ 6, 2 et 3 ;
▶ 6, 2 et 0 ;
▶ 6, 4 et 2.
▶ Compilez votre programme avec le flag -ggdb.
▶ Lancez votre programme avec le debugger gdb :
Que constatez-vous en ce qui concerne l’état du stack dans
les différents cas de figure ?
INFO-F105 13/21
Exercice 19
Le code C++ suivant introduit une boucle for en C++ :
1 # include < iostream >
2
3 using n a m e s p a c e std ;
4
5 int main () {
6 int i = 41;
7 int j = i ;
8 int k = 666;
9 int l = 0;
10 for ( int i = 40; i < 60; i += 2) {
11 int k = i ;
12 l = i ; i += 2; j = i ; i = j ;
13 cout << i << ’ ’ << j << endl ;
14 }
15 i ++;
16 cout << " apres la boucle for , i vaut : " << i << endl ;
17 cout << " apres la boucle for , j vaut : " << j << endl ;
18 cout << " apres la boucle for , k vaut : " << k << endl ;
19 cout << " apres la boucle for , l vaut : " << l << endl ;
20 if ( i != j ) {
21 i = 666;
22 int j = 666;
23 }
24
25 cout << " apres la condition if , i vaut : " << i << endl ;
26 cout << " apres la condition if , j vaut : " << j << endl ;
27 return 0;
28 }
INFO-F105 14/21
Exercice 19
Sans exécuter ce code, on vous demande de répondre aux
questions suivantes :
1. Combien d’itérations fait cette boucle for ?
2. Quelle est la valeur de i (ligne 16) ?
3. Quelle est la valeur de j (ligne 17) ?
4. Quelle est la valeur de k (ligne 18) ?
5. Quelle est la valeur de l (ligne 19) ?
6. Quelle est la valeur de i (ligne 25) ?
7. Quelle est la valeur de j (ligne 26) ?
INFO-F105 15/21
Exercice 20
Quelles sont les différences entre ce code-ci et le précédent ?
1 i = 41
2 j = i
3 k = 666
4 l = 0
5
6 for i in range (40 , 60 , 2) :
7 k = i
8 l = i
9 i += 2
10 j = i
11 i = j
12 print (i , end = ’ ’)
13 print ( j )
14
15 i += 1
16 print ( " La valeur de i apres la boucle for vaut :", i)
17 print ( " La valeur de j apres la boucle for vaut :", j)
18 print ( " La valeur de k apres la boucle for vaut :", k)
19 print ( " La valeur de l apres la boucle for vaut :", l)
20
21 if i != j :
22 i = 666
23 j = 666
24
25 print ( " La valeur de i apres la condition if vaut : " , i )
26 print ( " La valeur de j apres la condition if vaut : " , j )
INFO-F105 16/21
Exercice 21
Ecrivez un code C++ comprenant un exemple où deux variables
ont le même nom x et le même type mais dont le scope diffère.
Indiquez en commentaire pour chaque ligne de votre code
laquelle des deux variables sera désignée par le nom x. (Pour
votre facilité, dans les commentaires, vous pouvez les désigner
respectivement par x1 et x2.)
INFO-F105 17/21
Exercice 22
Les codes C++ ci-dessous utilisent les mots-clés extern et
static qui identifient des modes de stockage. Compilez avec
la commande suivante :
g++ file1.cpp file2.cpp -o out
Est-ce que cela fonctionne ? Supprimez le mot-clé extern dans
file2.cpp et recompilez. Cela fonctionne-t-il encore ?
Pourquoi ?
Décrivez l’output attendu après l’exécution du main et
réfléchissez sur le rôle des mots-clés.
INFO-F105 18/21
Exercice 22
1 # include < iostream >
2 using n a m e s p a c e std ;
3
4 int x ;
5
6 int func1 () {
7 int c = 0;
8 c ++; 1 # include < iostream >
9 return c ; 2 using namespace std ;
10 }
11 3 extern int x ;
12 int func2 () { 4
13 static int c = 0;
14 c ++; 5 int funct () {
15 return c ;
16 }
6 cout << x << endl ;
17 7 return 0;
18 int main () {
19 x = 3; 8 }
20 cout << func2 () << endl ;
21 cout << func2 () << endl ; ’file2.cpp’
22 cout << func1 () << endl ;
23 cout << func1 () << endl ;
24 return 0;
25 }
’file1.cpp’
INFO-F105 19/21
Exercice 23
Écrivez les fonctions f1 et f2 telles que :
▶ f1 stocke la valeur reçue en paramètre dans une variable
appelée x1, la décrémente et puis la renvoie. Nous voulons
que cette variable continue d’exister après l’exécution de
la fonction.
▶ La fonction f2 déclare une variable également appelée x1
en l’initialisant à 0 puis la retourne, après l’avoir
incrémentée, et ce sans changer la valeur de la variable
x1 de la fonction f1.
Quelle est la valeur de x2 dans le code suivant ?
1 int main () {
2 f1 (3) ;
3 int x2 = f1 ( f2 () ) ;
4 cout << x2 << endl ;
5 return 0;
6 }
INFO-F105 20/21
Questions
Avez-vous des doutes, des questions ou des
problèmes ?
Merci de votre attention et de votre
participation
INFO-F105 21/21