11. Classes d'allocation
En C, chaque variable (et chaque fonction) est caractérisée par son nom (cf. identificateur), son type, mais aussi sa classe d'allocation. La classe d'allocation indique au compilateur le type de mémoire nécessaire pour stocker la variable, ce qui détermine sa durée de vie et sa visibilité.
Notions de base
- Portée (scope) d'un identificateur
-
Région du texte du programme où sa déclaration est active; ceci implique que le compilateur
refusera un identificateur dont il n'a pas encore rencontré la déclaration.
- Identificateur top-level (global)
- Identificateur déclaré en dehors de toute fonction; sa portée va du point de déclaration à la fin du fichier
- Identificateur local
- Identificateur déclaré dans un bloc (ou fonction); sa portée va du point de déclaration jusqu'à la fin du bloc (ou fonction)
- Paramètre formel d'une fonction
- Sa portée va du point de déclaration dans la définition de la fonction jusqu'à la fin de la fonction
- Macro
- Sa portée débute par la déclaration de la macro #define nom et se termine à la fin du fichier (ou à la suppression de la macro #undef nom)
- Visibilité
- Propriété d'un identificateur indiquant si l'identificateur correspond à la déclaration de l'élément. Elle correspond à la portée, sauf si un identificateur de même nom est déclaré à un niveau plus interne. Une variable ne peut pas être utilisée tant qu'elle n'est plus visible, même si elle existe toujours (elle garde sa valeur). Par exemple, une variable top-level sera occultée par un variable locale de même nom.
- Classes de noms
-
Ensemble des identificateurs de même qualité. Un identificateur doit être unique à l'intérieur d'une classe de noms,
mais peut se retrouver dans différentes classes (bien que pareille pratique nuise à la lisibilité du code).
C reconnaît plusieurs classes de noms:
-
Les noms de macros
-
Les noms de labels
-
Les tags de struct, union, enum
-
Les noms de composants (champs des struct ou union)
-
Les autres noms (variables, fonctions...)
-
- Existence (ou durée de vie)
-
Période de temps durant laquelle de la mémoire est allouée à l'élément (variable ou fonction).
C propose trois types d'existence:
- Existence statique
- La mémoire est allouée au début du programme et libérée à la fin; la mémoire est réservée dans des segments de mémoire spécifiques.
- Existence locale
- La mémoire est allouée à l'entrée du bloc (ou fonction) et libérée à la sortie; la mémoire est réservée sur la pile du processeur.
- Existence dynamique
- La mémoire est allouée (dans le heap) à la demande, durant l'exécution de l'application, au moyen de la fonction malloc(), calloc(), ou realloc(). Elle est libérée grâce à la fonction free() ou à la fin de l'application.
Distinction entre déclaration et définition
La distinction entre déclaration et définition se marque au niveau de l'utilisation de la mémoire: on parle de définition (de variable ou de fonction) lorsque la mémoire est réservée pour cet élément, et de déclaration pour annoncer l'existence de l'identifiant (qui doit être défini ailleurs dans le code).
-
Pour les variables, la distinction est complexe, essentiellement pour les variables top-level.
Pour les variables locales, pas de problème: la mémoire étant allouée sur la pile, une déclaration n'est pas possible, seulement une définition le sera.
Pour les variables statiques, elles sont limitées à un seul fichier et la définition constituera déclaration. En effet, le mot-clé static limite la portée de la variable au seul fichier où elle est définie.
Les variables externes permettent quant à elles de déclarer une variable globale déjà définie ailleurs. Les variables sont déclarées externes en les préfixant avec le mot-clé extern (généralement dans des fichiers d'entête .h) ; par contre, la variable sera définie dans un seul fichier source (.c), en omettant le mot extern et en spécifiant un initiateur. Ainsi, pour que les deux fichiers sources main.c et produit.c partagent une variable entière global_a, on peut définir global_a dans produit.c sous la forme:
int global_a = 10; // définition (réservation d'espace mémoire)et y faire référence dans main.c par:
extern int global_a; // déclaration (pas de réservation d'espace mémoire) -
Pour les fonctions, la distinction est aisée : la définition est liée à l'écriture du code de la fonction, alors que la déclaration indique au compilateur les conditions d'utilisation de la fonction.
Pour les fonctions externes (mot-clé extern optionnel), la déclaration peut se mettre soit dans un fichier d'entête (.h) ou au top-level, au début du fichier source (.c).
Pour les fonctions statiques, la déclaration n'est jamais placée dans un fichier d'entête, étant donné que le mot-clé static a pour effet de limiter la portée de la définition d'une fonction au seul fichier où se trouve cette définition.
Attention, rappelons que les fonctions non déclarées sont considérées par le compilateur comme des fonctions de type int, sans paramètre: int fct().