Constante (programmation informatique)

valeur qui ne doit pas être modifiée par le programme lors de son exécution normale

En programmation informatique, une constante est une valeur qui ne doit pas être modifiée par le programme lors de son exécution normale, c'est-à-dire que la valeur est constante. Lorsqu'elle est associée à un identifiant, une constante est dite « nommée », bien que les termes « constante » et « constante nommée » soient souvent utilisés de manière interchangeable. Cela contraste avec une variable, qui est un identificateur dont la valeur peut être modifiée pendant l'exécution normale, c'est-à-dire que la valeur est variable. Les constantes sont utiles à la fois pour les programmeurs et les compilateurs : pour les programmeurs, elles constituent une forme de code auto-documenté et permettent de raisonner sur l'exactitude, tandis que pour les compilateurs, elles permettent des vérifications au moment de la compilation et de l'exécution qui vérifient que les hypothèses de constance ne sont pas violées, et permettent ou simplifier certaines optimisations du compilateur.

Il existe diverses réalisations spécifiques de la notion générale de constante, avec des distinctions subtiles qui sont souvent négligées. Les plus importantes sont : les constantes de compilation (à valeur statique), les constantes d'exécution (à valeur dynamique), les objets immuables et les types constants (const).

Des exemples typiques de constantes de compilation incluent des constantes mathématiques, des valeurs issues de normes (ici unité de transmission maximale ) ou des valeurs de configuration internes (ici caractères par ligne), comme ces exemples C :

const float PI = 3.1415927; // maximal single float precision
const unsigned int MTU = 1500; // Ethernet v2, RFC 894
const unsigned int COLUMNS = 80;

Des exemples typiques de constantes d'exécution sont des valeurs calculées en fonction des entrées d'une fonction, comme cet exemple C++ :

void f(std::string s) {
  const size_t l = s.length();
  // ...
}

Usage modifier

Certains langages de programmation font une distinction syntaxique explicite entre les symboles constants et variables, par exemple en considérant l'affectation à une constante comme une erreur de syntaxe, tandis que dans d'autres langages, ils sont considérés comme syntaxiquement identiques (tous deux simplement un identifiant), et la différence de traitement est sémantique (l'affectation à un identifiant est syntaxiquement valide, mais si l'identifiant est une constante, il est sémantiquement invalide).

Une valeur constante est définie une fois et peut être référencée plusieurs fois dans un programme. L'utilisation d'une constante au lieu de spécifier la même valeur plusieurs fois peut simplifier la maintenance du code (comme dans ne vous répétez pas ) et peut s'auto-documenter en fournissant un nom significatif pour une valeur, par exemple, PI au lieu de 3,1415926.

Comparaison avec les littéraux et les macros modifier

Il existe plusieurs façons principales d'exprimer une valeur de données qui ne change pas pendant l'exécution du programme et qui sont cohérentes dans une grande variété de langages de programmation. Une méthode très simple consiste à écrire simplement un nombre littéral, un caractère ou une chaîne dans le code du programme, ce qui est simple en C, C++ et autres langages similaires.

En langage d'assemblage, les nombres littéraux et les caractères sont effectués à l'aide des instructions de "mode immédiat" disponibles sur la plupart des microprocesseurs. Le nom "immédiat" vient du fait que les valeurs sont disponibles immédiatement à partir du flux d'instructions, au lieu de les charger indirectement en recherchant une adresse mémoire[1]. D'autre part, les valeurs plus longues que la longueur de mot du microprocesseur, telles que les chaînes et les tableaux, sont traitées indirectement et les assembleurs fournissent généralement une pseudo-opération "données" pour intégrer ces tables de données dans un programme.

Une autre méthode consiste à définir une macro symbolique. De nombreux langages de programmation de haut niveau et de nombreux assembleurs offrent une fonction de macro où le programmeur peut définir, généralement au début d'un fichier source ou dans un fichier de définition séparé, des noms pour différentes valeurs. Un préprocesseur remplace ensuite ces noms par les valeurs appropriées avant la compilation, ce qui donne quelque chose de fonctionnellement identique à l'utilisation de littéraux, avec les avantages de vitesse du mode immédiat. Parce qu'il peut être difficile de maintenir un code où toutes les valeurs sont écrites littéralement, si une valeur est utilisée de manière répétitive ou non évidente, cela se fait souvent comme une macro.

Une troisième méthode consiste à déclarer et à définir une variable comme étant "constante". Une variable globale ou une variable statique peut être déclarée, ce qui signifie que sa valeur sera définie au moment de la compilation et ne devrait pas être modifiable au moment de l'exécution. Les compilateurs placent généralement des constantes statiques dans la section texte d'un fichier objet avec le code lui-même, par opposition à la section données où les données initialisées non const sont conservées. Certains compilateurs peuvent produire une section spécifiquement dédiée aux constantes. La protection de la mémoire peut être appliquée à cette zone pour empêcher l'écrasement de telles constantes par des pointeurs errants.

Ces constantes diffèrent des littéraux de plusieurs façons. Les compilateurs placent généralement une constante dans un seul emplacement de mémoire identifié par un symbole, plutôt que de la répartir dans l'exécutable comme avec une macro. Bien que cela exclut les avantages de vitesse du mode immédiat, il existe des avantages en termes d'efficacité de la mémoire et les débogueurs peuvent travailler avec ces constantes lors de l'exécution. De même, alors que les macros peuvent être redéfinies accidentellement par des fichiers d'en-tête en conflit en C et C++, des constantes en conflit sont détectées au moment de la compilation.

Selon le langage, les constantes peuvent être non typées ou typées. En C et C++, les macros fournissent la première, tandis que const fournit la seconde :

#define PI 3.1415926535

const float pi2 = 3.1415926535;

tandis qu'en Ada, il existe des types numériques universels qui peuvent être utilisés, si vous le souhaitez :

pi : constant := 3.1415926535;

pi2 : constant float := 3.1415926535;

la variante non typée étant implicitement convertie dans le type approprié à chaque utilisation[2].

Constantes à valeur dynamique modifier

Outre les constantes statiques décrites ci-dessus, de nombreux langages procéduraux tels que Ada et C++ étendent le concept de constance aux variables globales créées au moment de l'initialisation, aux variables locales créées automatiquement lors de l'exécution sur la pile ou dans les registres, à la mémoire allouée dynamiquement qui est accessible par un pointeur et aux listes de paramètres dans les en-têtes de fonction.

Les constantes à valeur dynamique ne désignent pas une variable comme résidant dans une région spécifique de la mémoire, et les valeurs ne sont pas définies au moment de la compilation. Dans le code C++ tel que

float func(const float ANYTHING) {
    const float XYZ = someGlobalVariable*someOtherFunction(ANYTHING);
    ...
}

l'expression à laquelle la constante est initialisée n'est pas elle-même constante. L'utilisation de la constance n'est pas nécessaire ici pour la légalité du programme ou l'exactitude sémantique, mais présente trois avantages :

  1. Il est clair pour le lecteur que l'objet ne sera plus modifié, une fois défini
  2. Les tentatives de modification de la valeur de l'objet (par des programmeurs ultérieurs qui ne comprennent pas entièrement la logique du programme) seront rejetées par le compilateur
  3. Le compilateur peut être en mesure d'effectuer des optimisations de code en sachant que la valeur de l'objet ne changera pas une fois créé[3].

Les constantes à valeur dynamique ont été créées en tant que fonctionnalité de langage avec ALGOL 68[3] . Des études sur le code Ada et C++ ont montré que les constantes à valeur dynamique sont rarement utilisées, généralement pour 1 % ou moins des objets, alors qu'elles pourraient être utilisées beaucoup plus, car environ 40 à 50 % des objets locaux hors classe sont en fait invariants. une fois créé[3],[4]. D'un autre côté, ces "variables immuables" ont tendance à être la valeur par défaut dans les langages fonctionnels car elles favorisent les styles de programmation sans effet secondaire (par exemple, la récursivité) ou rendent la plupart des déclarations immuables par défaut, comme ML. Les langages purement fonctionnels interdisent même complètement les effets secondaires.

La constance est souvent utilisée dans les déclarations de fonctions, comme une promesse que lorsqu'un objet est passé par référence, la fonction appelée ne le changera pas. Selon la syntaxe, un pointeur ou l'objet pointé peut être constant, mais normalement ce dernier est souhaité. Surtout en C++ et C, la discipline consistant à s'assurer que les structures de données appropriées sont constantes tout au long du programme s'appelle const-correctness.

Paramètres de fonction constants modifier

En C/C++, il est possible de déclarer le paramètre d'une fonction ou d'une méthode comme constante. C'est une garantie que ce paramètre ne pourra pas être modifié après la première affectation (par inadvertance). Si le paramètre est un type prédéfini (intégré), il est appelé par valeur et ne peut pas être modifié. S'il s'agit d'un type défini par l'utilisateur, la variable est l'adresse du pointeur, qui ne peut pas non plus être modifiée. Cependant, le contenu de l'objet peut être modifié sans limites. Déclarer des paramètres comme des constantes peut être un moyen de signaler que cette valeur ne doit pas être modifiée, mais le programmeur doit garder à l'esprit que les vérifications sur la modification d'un objet ne peuvent pas être effectuées par le compilateur.

Outre cette fonctionnalité, il est également possible en C++ de déclarer une fonction ou une méthode en tant que const . Cela empêche ces fonctions ou méthodes de modifier autre chose que des variables locales.

En C#, le mot clé const existe, mais n'a pas le même effet pour les paramètres de fonction, comme c'est le cas en C/C++. Il existe cependant un moyen de "remuer" le compilateur pour effectuer la vérification, bien que ce soit un peu délicat[5].

Pour obtenir le même effet, d'abord, deux interfaces sont définies

public interface IReadable
{
    IValueInterface aValue { get; }
}

public interface IWritable : IReadable
{
    IValueInterface aValue { set; }
}

public class AnObject : IWritable
{
    private ConcreteValue _aValue;

    public IValueInterface aValue
    {
        get { return _aValue; }
        set { _aValue = value as ConcreteValue; }
    }
}

Ensuite, les méthodes définies sélectionnent la bonne interface avec des capacités de lecture seule ou de lecture/écriture :

public void doSomething(IReadable aVariable)
{
    // Cannot modify aVariable!
}

public void doSomethingElse(IWritable aVariable)
{
    // Can modify aVariable, so be careful!
}

Constantes orientées objet modifier

Une structure de données ou un objet constant est appelé « immuable » dans le langage orienté objet. Un objet étant immuable confère certains avantages dans la conception du programme. Par exemple, il peut être "copié" simplement en copiant son pointeur ou sa référence, évitant une opération de copie longue et économisant de la mémoire.

Les langages orientés objet tels que C++ étendent encore plus la constance. Les membres individuels d'une structure ou d'une classe peuvent être rendus const même si la classe ne l'est pas. Inversement, le mot clé mutable permet de modifier un membre de classe même si un objet a été instancié en tant que const.

Même les fonctions peuvent être const en C++. La signification ici est que seule une fonction const peut être appelée pour un objet instancié en tant que const ; une fonction const ne modifie aucune donnée non modifiable.

C # a à la fois un const et une readonly qualificatif; son const est uniquement pour les constantes de compilation, tandis que readonly peut être utilisé dans les constructeurs et autres applications d'exécution.

Java modifier

Java a un qualificatif appelé final qui empêche de modifier une référence et garantit qu'elle ne pointera jamais vers un objet différent. Cela n'empêche pas les modifications de l'objet référencé lui-même. La final de Java est fondamentalement équivalent à un pointeur const en C++. Il ne fournit pas les autres fonctionnalités de const.

En Java, la final qualificative indique que le membre de données ou la variable concerné(e) n'est pas attribuable, comme ci-dessous :

final int i = 3;
i = 4; // Error! Cannot modify a "final" object

Il doit être décidable par les compilateurs où la variable avec la final marker est initialisé et ne doit être exécuté qu'une seule fois, sinon la classe ne se compilera pas. La final de Java et les mots-clés const de C++ ont la même signification lorsqu'ils sont appliqués avec des variables primitives.

const int i = 3; // C++ declaration
i = 4; // Error!

Compte tenu des pointeurs, final fait référence en Java à quelque chose de similaire au pointeur const en C++. En C++, on peut déclarer un "type pointeur constant".

Foo *const bar = mem_location; // const pointer type

Ici, bar doit être initialisé au moment de la déclaration et ne peut plus être changé, mais ce qu'il pointe est modifiable. C'est-à-dire que {{{ 1 }}} est valide. Il ne peut tout simplement pas pointer vers un autre emplacement. Les références finales en Java fonctionnent de la même manière sauf qu'elles peuvent être déclarées non initialisées.

final Foo i; // a Java declaration

Remarque : Java ne prend pas en charge les pointeurs[6]. C'est parce que les pointeurs (avec des restrictions) sont le moyen par défaut d'accéder aux objets en Java, et Java n'utilise pas d'étoiles pour les indiquer. Par exemple, i dans le dernier exemple est un pointeur et peut être utilisé pour accéder à l'instance. On peut également déclarer un pointeur vers des données "en lecture seule" en C++.

const Foo *bar;

Ici, la bar peut être modifiée pour pointer n'importe quoi, n'importe quand; seule cette valeur pointée ne peut pas être modifiée via le pointeur de bar .

Il n'existe pas de mécanisme équivalent en Java. Ainsi, il n'y a pas non plus de méthodes const . L'exactitude const ne peut pas être appliquée en Java, bien qu'en utilisant des interfaces et en définissant une interface en lecture seule pour la classe et en la transmettant, on peut s'assurer que les objets peuvent être transmis autour du système de manière qu'ils ne puissent pas être modifiés.

Le framework de collections Java fournit un moyen de créer un wrapper immuable d'une [java]et méthodes similaires.

Une méthode en Java peut être déclarée "finale", ce qui signifie qu'elle ne peut pas être remplacée dans les sous-classes.

C# modifier

En C#, le qualificatif readonly a le même effet sur les données membres que final fait en Java et le const fait en C++ ; le modificateur const a un effet similaire (mais typé et de portée de classe) à celui de #define en C++. L'autre effet d'inhibition de l'héritage de Java final lorsqu'il est appliqué aux méthodes et aux classes est induit en C # à l'aide du mot-clé sealed.

Contrairement à C++, C# ne permet pas aux méthodes et aux paramètres d'être marqués comme const . Cependant, on peut également passer des sous-classes en lecture seule, et le .NET Framework fournit une certaine prise en charge pour la conversion de collections mutables en collections immuables qui peuvent être transmises en tant que wrappers en lecture seule.

Le traitement des constantes varie considérablement selon le paradigme de programmation. L'exactitude const est un problème dans les langages impératifs comme C++ car, par défaut, les liaisons de noms créent généralement des variables, qui peuvent varier, comme leur nom l'indique, et donc si l'on souhaite marquer une liaison comme constante, cela nécessite une indication supplémentaire. [note 1] Dans d'autres paradigmes de langage de programmation, des problèmes liés se posent, avec quelques analogues à la const-exactitude trouvés.

En programmation fonctionnelle, les données sont généralement constantes par défaut, plutôt que variables par défaut. Au lieu d'attribuer une valeur à une variable (un espace de stockage avec un nom et une valeur potentiellement variable), on crée une liaison d'un nom à une valeur, comme par la construction let dans de nombreux dialectes en Lisp. Dans certains langages fonctionnels, en particulier ceux multiparadigmes tels que Common Lisp, la modification des données est courante, alors que dans d'autres, elle est évitée ou considérée comme exceptionnelle ; c'est le cas de Scheme (un autre dialecte Lisp), qui utilise set! construire pour modifier les données, avec le! point d'exclamation attirant l'attention sur cela. Ces langages atteignent les objectifs de const-exactitude par défaut, attirant l'attention sur la modification plutôt que sur la constance.

Dans un certain nombre de langages orientés objet, il existe le concept d'un objet immuable, qui est particulièrement utilisé pour les types de base comme les chaînes ; les exemples notables incluent Java, JavaScript, Python et C#. Ces langages varient selon que les types définis par l'utilisateur peuvent être marqués comme immuables et peuvent permettre à des champs particuliers (attributs) d'un objet ou d'un type d'être marqués comme immuables.

Dans certains langages multiparadigmes qui autorisent à la fois les styles orientés objet et fonctionnels, ces deux fonctionnalités peuvent être combinées. Par exemple, dans OCaml, les champs d'objet sont immuables par défaut et doivent être explicitement marqués avec le mot-clé mutable pour être muables, tandis que dans Scala, les liaisons sont explicitement immuables lorsqu'elles sont définies avec val pour "valeur" et explicitement mutables lorsqu'elles sont définies avec var pour "variable ".

Conventions de nommage modifier

Les conventions de nommage des constantes varient. Certains les nomment simplement comme ils le feraient pour n'importe quelle autre variable. D'autres utilisent des majuscules et des traits de soulignement pour les constantes d'une manière similaire à leur utilisation traditionnelle pour les macros symboliques, telles que SOME_CONSTANT[7]. En notation hongroise, un préfixe "k" signifie des constantes ainsi que des macros et des types énumérés.

Une convention appliquée est qu'en Ruby, toute variable commençant par une lettre majuscule est considérée comme une constante, y compris les noms de classe.

Notes et références modifier

Notes modifier

  1. This is not universal: in Ada input parameters and loop parameters are implicitly constant, for instance.

Références modifier

  1. Ex. IBM Systems Information. Instruction Set - Assembler Language Reference for PowerPC.
  2. Grady Booch, Software Engineering with Ada, Benjamin Cummings, , 116–117 (ISBN 0-8053-0600-5, lire en ligne)
  3. a b et c Schilling, « Dynamically-Valued Constants: An Underused Language Feature », SIGPLAN Notices, vol. 30, no 4,‎ , p. 13–20 (DOI 10.1145/202176.202177)
  4. J. A. Perkins « Programming Practices: Analysis of Ada Source Developed for the Air Force, Army, and Navy » (DOI 10.1145/74261.74287)
    Proceedings TRI-Ada '89
  5. Timwi, « Read-only ("const"-like) function parameters of C# », https://stackoverflow.com/, Stack Overflow, (consulté le ) : « [...] Then you can declare methods whose parameter type “tells” whether it plans on changing the variable or not:. [...] This mimics compile-time checks similar to constness in C++. As Eric Lippert correctly pointed out, this is not the same as immutability. But as a C++ programmer I think you know that. »
  6. « Oracle Technology Network for Java Developers | Oracle Technology Network | Oracle », Java.sun.com, (consulté le )
  7. Microsoft Office XP Developer: Constant Names

Articles connexes modifier