Portée des variables: variables locales et globales

Nous avons vu dans les cours précédents que l’on pouvait déclarer des variables à différents endroits:

  • Directement dans une cellule Jupyter ou l’entête d’un programme:

        int a = 1;
    
  • Dans une boucle for:

        for ( int a = 0; i <= 5; i ++ ) {
            ...
        }
    
  • Comme paramètre d’une fonction:

        int f(int a) {
            ...
        }
    
  • Dans le corps d’une fonction:

         int f() {
             int a;
             ...
         }
    
  • Et en fait, plus généralement, dans un bloc:

         {
             int a;
             ...
         }
    

Quelques exemples

Dans un programme, le même nom peut être utilisé pour plusieurs variables, ce qui peut amener des ambiguïtés.

Devinez le résultat des commandes suivantes:

int i = 1;
{
    i = 2;
    i = i + 1;
}
i
int i = 1;
{
    int i = 2;
    i = i + 1;
}
i
int i = 1;
for ( int i = 1; i < 10; i++ ) {
    int i;
}
i

Exercice

Essayez par exemple, sans les exécuter, de prédire ce qui est affiché par les cinq cellules suivantes:

#include <iostream>
using namespace std;
int a = 0, b = 0;
void f(int b) {
    int c = 5;
    cout << "1: " << a << " " << b << " " << c << endl;
}
void g() {
    int a = 2, c = 2;
    cout << "2: " << a << " " << b << " " << c << endl;
    {
        long a = 3, c = 3;
        cout << "3: " << a << " " << b << " " << c << endl;
    }
    cout << "4: " << a << " " << b << " " << c << endl; 
    f(4);
}
g()

Exécutez maintenant les cellules; avez-vous le bon résultat?

La réponse pourrait en fait dépendre du langage! Pour prédire rigoureusement le comportement du programme, nous avons besoin de préciser la sémantique des variables et notamment leur portée.

La portée des variables

En C++, comme dans la plupart des langages modernes, la portée d’une variable (où elle est définie) est déterminée par le contexte lexical (comment le code est écrit) sans avoir à considérer le contexte d’exécution (comment le code s’exécute). Cela permet en effet de raisonner localement à une fonction, à un fichier, sans se préocuper du reste du programme.

Sémantique

  • Une variable est visible depuis sa déclaration jusqu’à la fin du bloc { ... } où elle est déclarée.

    Dans le cas d’une boucle for, le bloc comprend l’entête for (int i ....) { ... }.

  • Une variable peut masquer des variables issues des contextes englobants.

  • Une variable locale est définie dans un bloc (par exemple d’une fonction).

  • Un paramètre formel est défini dans l’entête d’une fonction; il se comporte comme une variable locale.

  • Une variable globale est définie ailleurs (cellule Jupyter, entête de programme)

Exercice

Reprenez l’exemple ci-dessus, et annotez chaque déclaration et chaque utilisation de variable: quelles variables y sont locales, globales, … Puis exécutez le pas-à-pas en dessinant la pile d’exécution pour prédire à nouveau l’affichage. Vérifiez.

Solution

int a = 0, b = 0;                  // Variables globales
void f(int b) {                    // Paramètre (donc local à f)
    int c = 5;                     // Variable locale de f
    // a: globale; b: paramètre de f; c: local à f
    cout << "1: " << a << " " << b << " " << c << endl;
}
void g() {
    int a = 2, c = 2;              // Variables locales à g
    // a, c: locales à g; b: globale
    cout << "2: " << a << " " << b << " " << c << endl;
    {
        long a = 3, c = 3;         // Variables locales au bloc
        // a, c: locales au bloc; b: globale
        cout << "3: " << a << " " << b << " " << c << endl;
    }
    // a, c: locales à g; b: globale
    cout << "4: " << a << " " << b << " " << c << endl; 
    f(4);
}
g()

Rappels

  • Une variable locale à une fonction n’existe que le temps d’exécution de la fonction

  • La valeur de cette variable d’un appel à la fonction est perdue lors du retour au programme appelant et ne peut pas être récupérée lors d’un appel ultérieur

Variables globales: bonnes pratiques

  • Accessible à l’intérieur de toutes les fonctions

  • On peut modifier la valeur d’une variable globale
    Ceci est déconseillé (effet de bord)

  • Une variable locale masque une variable globale du même nom
    Ceci est déconseillé (ambiguïté à la lecture rapide)

  • On évitera ces pratiques dans le cadre de ce cours

Résumé

  • Une variable est définie depuis sa déclaration jusqu’à la fin du bloc la contenant
    (en considérant l’entête d’une fonction ou d’une boucle for comme dans le bloc)

  • Une variable hors de tout bloc est dite globale
    En général, on évite les variables globales

  • Sinon elle est locale