3.2. Les classes de stockage
Les variables C/C++ peuvent être créées de différentes manières. Il est courant,
selon la manière dont elles sont créées et la manière dont elles pourront être utilisées, de les classer
en différentes catégories de variables. Les différents aspects que peuvent prendre les variables
constituent ce que l'on appelle leur classe de stockage.
La classification la plus simple que l'on puisse faire des variables est
la classification locale - globale. Les variables globales sont déclarées en dehors
de tout bloc d'instructions, dans la zone de déclaration globale du programme. Les variables
locales en revanche sont créées à l'intérieur d'un bloc d'instructions.
Les variables locales et globales ont des durées de vie, des portées et des emplacements en mémoire
différents.
La portée d'une variable est la zone du programme
dans laquelle elle est accessible. La portée des variables globales est tout le programme, alors que
la portée des variables locales est le bloc d'instructions dans lequel elles ont été créées.
La durée de vie d'une variable est le temps pendant lequel
elle existe. Les variables globales sont créées au début du programme et détruites à la fin, leur durée
de vie est donc celle du programme. En général, les variables locales ont une durée de vie qui va
du moment où elles sont déclarées jusqu'à la sortie du bloc d'instructions dans lequel elles ont été
déclarées. Cependant, il est possible de faire en sorte que les variables locales survivent à la sortie
de ce bloc d'instructions. D'autre part, la portée d'une variable peut commencer avant sa durée de vie
si cette variable est déclarée après le début du bloc d'instructions dans lequel elle est déclarée.
La durée de vie n'est donc pas égale à la portée d'une variable.
La classe de stockage d'une variable permet de spécifier
sa durée de vie et sa place en mémoire (sa portée est toujours
le bloc dans lequel la variable est déclarée). Le C/C++ dispose d'un éventail de classes de stockage
assez large et permet de spécifier le type de variable que l'on désire utiliser :
auto : la classe de stockage par défaut.
Les variables ont pour portée le bloc d'instructions dans lequel elles ont été crées. Elles ne sont
accessibles que dans ce bloc. Leur durée de vie est restreinte à ce bloc. Ce mot clé est facultatif,
la classe de stockage auto étant la classe par défaut ;
static : cette classe de stockage permet
de créer des variables dont la portée est le bloc d'instructions en cours, mais qui, contrairement
aux variables auto, ne sont pas détruites lors de la sortie de ce bloc. À chaque fois
que l'on rentre dans ce bloc d'instructions, les variables statiques existeront et auront pour valeurs
celles qu'elles avaient avant que l'on quitte ce bloc. Leur durée de vie est donc celle du programme,
et elles conservent leurs valeurs. Un fichier peut être considéré comme un bloc. Ainsi, une variable
statique d'un fichier ne peut pas être accédée à partir d'un autre fichier. Cela est utile en compilation
séparée (voir plus loin) ;
register : cette classe de stockage
permet de créer une variable dont l'emplacement se trouve dans un registre du microprocesseur. Il faut
bien connaître le langage machine pour correctement utiliser cette classe de variable. En pratique,
cette classe est très peu utilisée ;
volatile : cette classe de variable sert
lors de la programmation système. Elle indique qu'une variable peut être modifiée en arrière-plan
par un autre programme (par exemple par une interruption, par un thread, par un autre processus,
par le système d'exploitation ou par un autre processeur dans une machine parallèle). Cela nécessite
donc de recharger cette variable à chaque fois qu'on y fait référence dans un registre du processeur,
et ce même si elle se trouve déjà dans un de ces registres
(ce qui peut arriver si on a demandé au compilateur d'optimiser le programme) ;
extern : cette classe est utilisée
pour signaler que la variable peut être définie dans un autre fichier. Elle est utilisée dans le cadre
de la compilation séparée (voir le Chapitre 6 pour plus de détails).
Il existe également des modificateurs pouvant s'appliquer à une variable
pour préciser sa constance :
const : ce mot clé est utilisé
pour rendre le contenu d'une variable non modifiable. En quelque sorte, la variable devient ainsi
une variable en lecture seule. Attention, une telle variable n'est pas forcément une constante :
elle peut être modifiée soit par l'intermédiaire d'un autre identificateur, soit par une entité
extérieure au programme (comme pour les variables volatile). Quand ce mot clé
est appliqué à une structure, aucun des champs de la structure n'est accessible en écriture. Bien
qu'il puisse paraître étrange de vouloir rendre « constante » une « variable »,
ce mot clé a une utilité. En particulier, il permet de faire du code plus sûr ;
mutable : disponible uniquement en C++,
ce mot clé ne sert que pour les membres des structures. Il permet de passer outre la constance éventuelle
d'une structure pour ce membre. Ainsi, un champ de structure déclaré mutable peut être
modifié même si la structure est déclarée const.
Pour déclarer une classe de stockage particulière, il suffit de faire précéder
ou suivre le type de la variable par l'un des mots clés auto, static,
register, etc. On n'a le droit de n'utiliser que les classes de stockage
non contradictoires. Par exemple, register et extern sont
incompatibles, de même que register et volatile, et
const et mutable. Par contre, static et
const, de même que const et volatile, peuvent
être utilisées simultanément.
Exemple 3-13. Déclaration d'une variable locale statique
int appels(void)
{
static int n = 0;
return n = n+1;
} Cette fonction mémorise le nombre d'appels qui lui ont été faits dans la variable
n et renvoie ce nombre. En revanche, la fonction suivante :
renverra toujours
1. En effet, la variable
n est créée, initialisée,
incrémentée et détruite à chaque appel. Elle ne survit pas à la fin de l'instruction
return.
Exemple 3-14. Déclaration d'une variable constante
const int i=3;
i prend la valeur 3 et ne peut plus être
modifiée.
Les variables globales qui sont définies sans le mot clé const
sont traitées par le compilateur comme des variables de classe de stockage extern
par défaut. Ces variables sont donc accessibles à partir de tous les fichiers du programme. En revanche,
cette règle n'est pas valide pour les variables définies avec le mot clé const.
Ces variables sont automatiquement déclarées static par le compilateur, ce qui
signifie qu'elles ne sont accessibles que dans le fichier dans lequel elles ont été déclarées.
Pour les rendre accessibles aux autres fichiers, il faut impérativement les déclarer avec le mot clé
extern avant de les définir.
Exemple 3-15. Déclaration de constante externes
int i = 12; /* i est accessible de tous les fichiers. */
const int j = 11; /* Synonyme de "static const int j = 11;". */
extern const int k; /* Déclare d'abord la variable k... */
const int k = 12; /* puis donne la définition. */
Notez que toutes les variables définies avec le mot clé const
doivent être initialisées lors de leur définition. En effet, on ne peut pas modifier la valeur
des variables const, elles doivent donc avoir une valeur initiale. Enfin,
les variables statiques non initialisées prennent la valeur nulle.
Les mots clés const et volatile demandent
au compilateur de réaliser des vérifications additionnelles lors de l'emploi des variables qui ont
ces classes de stockage. En effet, le C/C++ assure qu'il est interdit de modifier (du moins sans
magouiller) une variable de classe de stockage const, et il assure également
que toutes les références à une variable de classe de stockage volatile se feront
sans optimisations dangereuses. Ces vérifications sont basées sur le type des variables manipulées.
Dans le cas des types de base, ces vérifications sont simples et de compréhension immédiate. Ainsi,
les lignes de code suivantes :
génèrent une erreur parce qu'on ne peut pas affecter une valeur de type
int à une variable
de type
const int.
En revanche, pour les types complexes (pointeurs et références en particulier),
les mécanismes de vérifications sont plus fins. Nous verrons quels sont les problèmes soulevés
par l'emploi des mots clés const et volatile avec les pointeurs
et les références dans le chapitre traitant des pointeurs.
Enfin, en C++ uniquement, le mot clé mutable permet de rendre
un champ de structure const accessible en écriture :
Exemple 3-16. Utilisation du mot clé mutable
struct A
{
int i; // Non modifiable si A est const.
mutable int j; // Toujours modifiable.
};
const A a={1, 1}; // i et j valent 1.
int main(void)
{
a.i=2; // ERREUR ! a est de type const A !
a.j=2; // Correct : j est mutable.
return 0;
}