Informatique et Sciences du Numérique au lycée : un pas plus loin/LANGAGES/Éléments de syntaxe et de sémantique/Éléments de base/Déclarations
Dans un source de programme, une déclaration introduit un identificateur et précise éventuellement son type. La définition de cet identificateur lui associe une valeur . La déclaration construit une liaison entre l'identificateur et son type, la définition établit une liaison entre l'identificateur et sa valeur. Ces liaisons sont utilisées par le compilateur. Par exemple, le fragment d'un programme C suivant
int A;
constitue la définition de l'identificateur A, lié à une valeur qui est l'adresse d'une zone mémoire pouvant stocker un entier.
Le fragment de programme OCaml contient la définition de l'identificateur K, lié à l'entier 25. Cette liaison n'est pas modifiable et K peut être appelé constante. La définition de B lie B à une adresse-mémoire où est stockée la valeur 1. La liaison de B à cette adresse-mémoire n'est pas non plus modifiable. En revanche, la valeur stockée à l'adresse mémoire dénotée par B peut être modifiée par une affectation, tout comme peut l'être la valeur stockée à l'adresse-mémoire dénotée par A.
let K = 25 ;;
let B= ref 1;;
Le type de A dans le programme C doit être déclaré. Ceux de K et de B ne nécessitent pas d'être précisés, ils sont déduits (inférés) par l'algorithme de typage de OCaml à partir des valeurs fournies dans leurs définitions.
Un identificateur lié à une adresse-mémoire est généralement appelé variable (A et B dans l'exemple précédent sont des variables) et la valeur stockée à l'adresse-mémoire est modifiable par une opération d' affectation ou une opération dérivée. Un identificateur lié à une valeur exprimable par un littéral est appelée constante (K est une constante).
Selon les syntaxes des langages, une déclaration peut n'introduire qu'un identificateur et éventuellement le type de la valeur pouvant lui être lié. Dans ce cas, la valeur liée à cet identificateur doit être fournie avant toute utilisation de cet identificateur par une définition. À l'opposé, un langage peut ne pas fournir de construction syntaxique pour les déclarations, la première occurrence dans le texte d'un identificateur étant interprétée comme sa déclaration. Assez souvent, la définition d'un identificateur effectue en même temps sa déclaration. En revanche, pour les fonctions, les déclarations sont en général distinctes des définitions : les déclarations sont faites dans la partie parfois appelée en-tête de la fonction, la définition étant donnée par le corps de la fonction.
Quelle que soit la nature de la valeur liée, pour pouvoir raisonner sur le code d'un programme, il faut faire l'hypothèse qu'une liaison n'est pas modifiable et que sa validité peut être déduite de la structure du programme (pour aller plus loin, voir Sémantiques). On appelle portée d'un identificateur la durée de validité de la liaison qui lui est associée. Celle-ci est déterminée par des règles syntaxiques propres à chaque langage. Dans une première approche, on peut distinguer deux catégories de déclarations, celle dite locale si sa portée est celle d'une portion du source, celle dite globale si sa portée est celle du texte source complet. Une liaison valide peut être masquée s'il est possible de faire une nouvelle déclaration avec le même identificateur. Le masquage dure le temps de la durée de la liaison masquante. Un ensemble des liaisons est appelé environnement; l'évaluation des expressions se fait en fait dans un environnement contenant les liaisons accessibles, c'est-à-dire valides et non masquées, au moment de cette évaluation. Certains langages peuvent accepter plusieurs déclarations du même identificateur sans considérer qu'il y a masquage, si ces déclarations peuvent être distinguées grâce à d'autres informations comme le type par exemple. On parle alors de surcharge de l'identificateur. Chacune de ces déclarations introduit une nouvelle liaison. Pour chaque occurrence de l'identificateur surchargé, le compilateur a à choisir la liaison correcte et refuser de choisir en cas d'ambiguïté. La surcharge est surtout utilisée pour les opérateurs, fonctions et procédures ainsi que pour les méthodes des langages à classes ou objet. Tout langage de programmation possède ses propres règles de portée, de visibilité et de surcharge.
L'ensemble des identificateurs déclarés dans un texte source, appelé parfois en compilation espace de noms, devient vite très peuplé. Il est donc nécessaire de structurer le texte source en parties indépendantes pour en diminuer la taille et éventuellement partager des parties de code. C'est le rôle des modules, des objets et des classes. Les notions de portée et visibilité sont alors raffinées, un identificateur peut alors incorporer le nom du module ou de la classe où il est déclaré. Par exemple, la liaison de l'identificateur cons
déclarée dans le module List
de Ocaml peut être utilisée par d'autres modules sous le nom List.cons
. À l'intérieur du module List
, la liaison peut être utilisée sous le nom de cons
. De même, certaines liaisons peuvent être masquées hors certaines régions des programmes objet en délimitant celles-ci avec le mot-clé private
. La signification d'un tel mot-clé varie d'un langage à l'autre et il est important de s'assurer de sa sémantique dans le langage choisi et dans la région du programme (contexte d'utilisation) où il figure.
Revenons sur la notion de fermeture introduite dans la section Expressions, fonctions et valeurs. Rappelons qu'une fermeture est la valeur d'une fonction et que pour évaluer son application à un paramètre effectif, il est nécessaire non seulement de connaitre son code mais aussi les valeurs des identificateurs (variables libres, opérateurs) figurant dans le corps de la fonction. Ces valeurs peuvent être retrouvées par les liaisons de leurs identificateurs mais dans quel environnement ? Celui présent lors de la définition de la fonction ou celui présent au moment de l'évaluation de l'une de ses applications ? Ces environnements peuvent différer parce que certaines liaisons présentes lors de la définition sont masquées ou n'existent plus au moment d'une des applications. Dans le premier cas, le langage est dit à portée statique et la fermeture inclut (conserve un accès à) l'environnement présent au moment de la définition de la fonction, dans le second, le langage est dit à portée dynamique, la valeur des variables libres peut différer d'une application à l'autre voire ne pas exister.
Effet de l'affectation sur l'environnement