©
D. Mathieu
mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique
Créé le 14/09/2001 -
Dernière mise à jour : 14/09/2001
Spécifieur autoObjet statique
Spécifieur register
Déclaration/définition globaleObjet statique et espace de noms
Déclaration/définition globale avec initialisation
Déclarations globales multiples, lien externe
Déclarations globales multiples, lien interne
Déclaration locale, lien externe
Déclaration locale, lien interne
Espace de noms anonyme
Espace de noms nommé
Le C/C++ offre deux classes de stockage : automatique et statique.
void f ()
{ int i; // début de la portée, de la durée de vie et de la visibilité de i // ... if (...) { int i; // nouvelle variable i qui masque la précédente définition // ... } // fin de la portée, de la durée de vie et de la visibilité de la // variable interne i. La première variable i redevient visible // ... } // fin de la portée, de la durée de
vie et de la visibilité
|
Exemple 1
Une variable automatique n'est jamais initialisée par défaut (sauf bien sûr s'il s'agit d'un objet d'une classe, dont le constructeur est appelé).
void f ()
{ auto int i; // début de la portée et de la visibilité de i // ... } // fin de la durée de vie et de la visibilité de la variable i. |
Exemple 2
Les variables automatiques sont par défaut stockées dans la pile d'exécution du processus.
// fichier main.cxx
// ... int Global; int main (...)
} // main() |
Exemple 3
La variable Global est visible de la totalité du fichier main.cxx à partir de la ligne où elle est déclarée. Cette déclaration est aussi une définition : un espace mémoire lui est allouée dans un segment de mémoire virtuelle dite mémoire statique (sous Unix, ce segment est appelé segment bss : voir la Gestion de la mémoire sous Linux). Enfin, conformément à la norme C++, tous les octets sont mis à 0, la variable Global est donc initialisée à 0.
Lors du lancement du programme, toutes les variables globales sont chargées avant que le contrôle ne soit donné à la fonction main().
// fichier main.cxx
// ... int Global = 12; struct SObj
SObj ObjGlobal; int main ()
} // main() |
Exemple 4
provoque l'affichage :
Constr. : 12
Debut de main() |
Comme on le voit, l'objet ObjGlobal est construit avant l'exécution de la première instruction de la fonction main(), et le constructeur utilise la valeur de la variable Global déjà initialisée.
Bien que non obligatoire, le préfixage des objets globaux par :: est fortement recommandé pour faciliter la compréhension.
// fichier main.cxx
// ... int Global; extern void f1(); // déclaration de la fonction f1() int main (...)
} // main() |
// fichier f1.cxx
// ... int Global; void f1()
} // f1() |
duo> g++ main.cxx f1.cxx
/tmp/ccMlT3IR.o(.bss+0x0): multiple definition of `Global' /tmp/ccNTPdhr.o(.bss+0x4): first defined here |
Exemple 5
Il faut donc découpler la déclaration de la définition, ce qui ne peut être obtenu que par l'utilisation du mot réservé extern : toutes les déclarations d'un objet dans les différentes unités de compilation doivent donc être précédées du spécifieur de classe de stockage extern, sauf l'une d'entre elles qui devient de ce fait une déclaration/définition.
Il va de soi que, si la variable doit être explicitement initialisée (c'est-à-dire autrement que par 0), c'est au moment de sa définition (réservation de la mémoire) et non de sa déclaration qu'elle doit l'être.
L'exemple ci-dessus devient :
// fichier main.cxx
// ... extern int Global; // déclaration extern void f1(); int main (...)
} // main() |
// fichier f1.cxx
// ... int Global = 12; // déclaration/définition void f1() { cout << "Global dans f1 = " << ++Global << endl; } |
Exemple 6
qui provoque l'affichage :
++Global = 13
Global dans f1 = 14 |
On dit que la présence du spécifieur extern crée un lien externe à la variable Global.
La portée, la visibilité et la durée de vie se confondent, sauf si la visibilité de l'identificateur est provisoirement masquée par la déclaration d'un autre objet homonyme.
// fichier main.cxx
// ... extern int Global; // déclaration extern void f1();
int main (...)
} // main() |
// fichier f1.cxx
// ... int Global = 12; // déclaration/définition void f1() { cout << "Global dans f1 = " << ++Global << endl; } |
// fichier f2.cxx
// ... static int Global = 22; // déclaration/définition void f2() { cout << "Global dans f2 = " << ++Global << endl; } |
// fichier f3.cxx
// ... static int Global = 32; // déclaration/définition void f3() { cout << "Global dans f3 = " << ++Global << endl; } |
Exemple 7
qui provoque l'affichage :
Global dans main = 13
Global dans f1 = 14 Global dans f2 = 23 Global dans f3 = 33 |
Comme on le voit, il y a bien trois objets Global dans le programme, ceux des fichiers f2.cxx et f3.cxx masquant provsoirement celui de main.cxx et f1.cxx.
On dit que la présence du spécifieur static crée un lien interne à la variable Global.
// fichier main.cxx
// ... int Global = 12; // déclaration/définition extern void f1();
int main (...)
} // main() |
// fichier f12.cxx
// ... void f1()
cout << "Global dans f1 = " << ++Global << endl; } // f1() void f2() { cout << "Global dans f2 = " << ++Global << endl; } |
Exemple 8
Une erreur de syntaxe est signalée dans la fonction f2(), dans laquelle Global n'est pas déclarée.
L'exemple suivant présente un cas plus surprenant :
// fichier main.cxx
// ... int Global = 12; // déclaration/définition extern void f1();
int main (...)
} // main() |
// fichier f12.cxx
// ... void f1()
cout << "Global dans f1 = " << ++Global << endl; } // f1() static int Global = 32; // déclaration/définition void f2()
} // f2() |
Exemple 9
dont l'exécution est la suivante :
Global dans main = 13
Global dans f1 = 33 Global dans f2 = 34 |
Contrairement à ce que l'on pourrait attendre, la variable Global utilisée dans la fonction f2() est celle qui est locale au fichier f12.cxx, bien qu'elle soit définie plus loin. Le spécifieur extern qui précède la déclaration de Global dans la fonction f1() introduit un lien externe, mais cela ne signifie pas obligatoirement "externe à l'unité de compilation". Il faut plutôt l'interpréter comme "défini ailleurs ... ou plus loin" !
// fichier main.cxx
// ... void f1 ()
cout << "Global dans f1 = " << ++Global << endl; } // f1() int main (...)
for (int i = 3; --i; ) f1(); } // main() |
Exemple 10
L'affichage dans la fonction main() est en commentaire car elle provoque une erreur de syntaxe : Global n'est pas déclarée.
L'exécution de ce programme est la suivante :
Global dans f1 = 13
Global dans f1 = 14 Global dans f1 = 15 |
Malgré les apparences, la variable Global n'est pas une variable automatique, stockée dans la pile au moment de l'appel de f1() et détruite à sa sortie. C'est au contraire une variable globale, déclarée/définie lors du premier appel de la fonction, et dont l'existence perdure au-delà de la sortie.
Attention : l'initialisation ne peut être dissociée de la déclaration, car elle serait effectuée à chaque appel, comme dans le cas suivant :
// fichier main.cxx
// ... void f1 ()
cout << "Global dans f1 = " << ++Global << endl; } // f1() // ... |
Exemple 11
ce qui ne présenterait plus aucun intérêt.
// fichier main.cxx
// ... extern void f1();
int main (...)
} // main() |
// fichier f12.cxx
// ... namespace { int Global = 12; // déclaration/définition } // namespace anonyme void f1() { cout << "Global dans f1 = " <<
++Global << endl; }
|
dont l'exécution provoque l'affichage suivant :
Global dans f1 = 13
Global dans f2 = 14 |
Exemple 12
Comme précédemment, l'affichage dans la fonction main() est en commentaire car elle provoque une erreur de syntaxe : Global n'est pas déclarée.
Tout se passe comme si Global était déclaré précédé de static.
// fichier main.cxx
// ... namespace nsGlobal { int Global = 12; } extern void f1();
using namespace nsGlobal; int main ()
return 0; } // main()
|
// fichier f12.cxx
// ... namespace nsGlobal { extern int Global; } void f1()
} // f1() static int Global = 22; void f2()
} // f2() |
dont l'exécution provoque l'affichage suivant :
Global dans main = 13
Global dans f1 = 14 Global dans f2 = 23 |
Exemple 13
Remarque : l'affichage de Global dans la fonction f2() serait mieux écrit :
cout << "Global dans f2 = " << ++::Global
<< endl;
// -- |
© D. Mathieu
mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique