Que signifie «const static» en C et C ++?

const static int foo = 42; 

J’ai vu ceci dans un code ici sur StackOverflow et je ne pouvais pas comprendre ce qu’il fait. Ensuite, j’ai vu des réponses confuses sur d’autres forums. Ma meilleure supposition est qu’elle est utilisée en C pour masquer le foo constant des autres modules. Est-ce correct? Si oui, pourquoi quelqu’un l’utiliserait-il dans un contexte C ++ où vous pouvez simplement le rendre private ?

Il a des utilisations à la fois en C et en C ++.

Comme vous l’avez deviné, la partie static limite sa scope à cette unité de compilation . Il permet également l’initialisation statique. const dit simplement au compilateur de ne laisser personne le modifier. Cette variable est soit placée dans le segment de données ou bss en fonction de l’architecture, et peut être marquée en mémoire en lecture seule.

Tout ceci explique comment C traite ces variables (ou comment C ++ traite les variables d’espace de noms). En C ++, un membre marqué static est partagé par toutes les instances d’une classe donnée. Que ce soit privé ou non n’affecte pas le fait qu’une variable est partagée par plusieurs instances. Avoir const on là vous avertira si un code essaierait de le modifier.

S’il était ssortingctement privé, chaque instance de la classe obtiendrait sa propre version (nonobstant l’optimiseur).

Beaucoup de gens ont donné la réponse de base, mais personne n’a fait remarquer qu’en C ++, la valeur par défaut était static au niveau de l’ namespace de namespace (et certaines donnaient des informations erronées). Voir la norme standard C ++ 98 3.5.3.

D’abord quelques fondements:

Unité de traduction: Un fichier source après le pré-processeur (de manière récursive) comprenait tous ses fichiers include.

Liaison statique: Un symbole est uniquement disponible dans son unité de traduction.

Lien externe: Un symbole est disponible à partir d’autres unités de traduction.

Au niveau de l’ namespace

Cela inclut l’espace de noms global alias les variables globales .

 static const int sci = 0; // sci is explicitly static const int ci = 1; // ci is implicitly static extern const int eci = 2; // eci is explicitly extern extern int ei = 3; // ei is explicitly extern int i = 4; // i is implicitly extern static int si = 5; // si is explicitly static 

Au niveau de la fonction

static signifie que la valeur est maintenue entre les appels de fonction.
La sémantique des variables static de la fonction est similaire aux variables globales en ce sens qu’elles résident dans le segment de données du programme (et non dans la stack ou le tas), consultez cette question pour plus de détails sur la durée de vie des variables static .

Au niveau de la class

static signifie que la valeur est partagée entre toutes les instances de la classe et que const signifie qu’elle ne change pas.

Cette ligne de code peut en fait apparaître dans plusieurs contextes différents et, bien qu’elle se comporte approximativement de la même manière, il existe de petites différences.

Étendue de l’espace de noms

 // foo.h static const int i = 0; 

i ” sera visible dans chaque unité de traduction incluant l’en-tête. Cependant, à moins que vous n’utilisiez réellement l’adresse de l’object (par exemple, ” &i “), je suis sûr que le compilateur traitera ” i ” simplement comme un type 0 sûr. Lorsque deux unités de traduction supplémentaires prennent le ” &i “, l’adresse sera différente pour chaque unité de traduction.

 // foo.cc static const int i = 0; 

i ” a un lien interne et ne peut donc pas être appelé de l’extérieur de cette unité de traduction. Cependant, encore une fois, sauf si vous utilisez son adresse, il sera probablement traité comme un 0 type sécurisé.

Une chose qui mérite d’être signalée est la suivante:

 const int i1 = 0; 

est exactement le même que static const int i = 0 . Une variable dans un espace de noms déclaré avec const et non explicitement déclarée avec extern est implicitement statique. Si vous pensez à cela, le comité C ++ avait l’intention d’autoriser la déclaration des variables const dans les fichiers d’en-tête sans avoir toujours besoin du mot-clé static pour éviter de rompre le ODR.

Portée de la classe

 class A { public: static const int i = 0; }; 

Dans l’exemple ci-dessus, la norme spécifie explicitement qu’il n’est pas nécessaire de définir « i » si son adresse n’est pas requirejse. En d’autres termes, si vous n’utilisez ” i ” que comme type 0, le compilateur ne le définira pas. Une différence entre les versions de classe et d’espace de noms est que l’adresse de « i » (si elle est utilisée dans deux ou plusieurs unités de traduction) sera la même pour le membre de la classe. Lorsque l’adresse est utilisée, vous devez en avoir une définition:

 // ah class A { public: static const int i = 0; }; // a.cc #include "ah" const int A::i; // Definition so that we can take the address 

C’est une petite optimisation de l’espace.

Quand tu dis

 const int foo = 42; 

Vous ne définissez pas une constante, mais créez une variable en lecture seule. Le compilateur est assez intelligent pour utiliser 42 à chaque fois qu’il voit foo, mais il alloue également de l’espace dans la zone de données initialisée. Ceci est fait parce que, comme défini, foo a un lien externe. Une autre unité de compilation peut dire:

extern const int foo;

Pour accéder à sa valeur. Ce n’est pas une bonne pratique puisque cette unité de compilation n’a aucune idée de la valeur de foo. Il sait juste que c’est un const int et doit recharger la valeur depuis la mémoire chaque fois qu’il est utilisé.

Maintenant, en déclarant que c’est statique:

 static const int foo = 42; 

Le compilateur peut faire son optimisation habituelle, mais il peut aussi dire “Hé, personne en dehors de cette unité de compilation ne peut voir foo et je sais que c’est toujours 42 alors il n’y a pas besoin d’allouer de l’espace pour cela.”

Je devrais également noter qu’en C ++, la méthode préférée pour empêcher les noms d’échapper à l’unité de compilation en cours consiste à utiliser un espace de noms anonyme:

 namespace { const int foo = 42; // same as static definition above } 

Il manque un “int”. CA devrait etre:

 const static int foo = 42; 

En C et C ++, il déclare une constante entière avec une étendue de fichier locale de valeur 42.

Pourquoi 42? Si vous ne le savez pas déjà (et c’est difficile à croire), c’est une référence à la réponse à la vie, à l’univers et à tout .

En C ++,

 static const int foo = 42; 

est le moyen privilégié pour définir et utiliser des constantes. Ie utiliser plutôt que

 #define foo 42 

parce qu’il ne subvertit pas le système de sécurité de type.

Pour toutes les bonnes réponses, je veux append un petit détail:

Si vous écrivez des plugins (par exemple des DLL ou des bibliothèques .so à charger par un système de CAO), alors statique est un économiseur de vie qui évite les collisions de noms comme celui-ci:

  1. Le système de CAO charge un plugin A, qui a un “const int foo = 42;” dedans.
  2. Le système charge un plugin B, qui a “const int foo = 23;” dedans.
  3. En conséquence, le plugin B utilisera la valeur 42 pour foo, car le plugin loader réalisera qu’il existe déjà un “foo” avec un lien externe.

Pire encore: l’étape 3 peut se comporter différemment selon l’optimisation du compilateur, le mécanisme de chargement du plug-in, etc.

J’ai eu ce problème une fois avec deux fonctions d’aide (même nom, comportement différent) dans deux plugins. Les déclarer statiques a résolu le problème.

Oui, il cache une variable dans un module des autres modules. En C ++, je l’utilise quand je ne veux pas / je n’ai pas besoin de changer un fichier .h qui déclenchera une reconstruction inutile des autres fichiers. Aussi, je mets le statique en premier:

 static const int foo = 42; 

De plus, en fonction de son utilisation, le compilateur ne lui allouera même pas de mémoire et “insérera” simplement la valeur utilisée. Sans le statique, le compilateur ne peut pas supposer qu’il n’est pas utilisé ailleurs et ne peut pas être intégré.

Cette constante globale est visible / accessible uniquement dans le module de compilation (fichier .cpp). BTW en utilisant statique à cet effet est obsolète. Mieux vaut utiliser un espace de noms anonyme et une énumération:

 namespace { enum { foo = 42 }; } 

Le rendre privé signifierait toujours qu’il apparaît dans l’en-tête. J’ai tendance à utiliser la manière la plus faible qui fonctionne. Voir cet article classique de Scott Meyers: http://www.ddj.com/cpp/184401197 (il concerne les fonctions, mais peut également être appliqué ici).

Selon la spécification C99 / GNU99:

  • static

    • est spécificateur de classe de stockage

    • objects de scope de niveau fichier par défaut a lien externe

    • objects de scope de niveau fichier avec spécificateur statique a lien interne
  • const

    • est qualificatif de type (fait partie du type)

    • mot-clé appliqué à l’instance immédiate de gauche – c.-à-d.

      • MyObj const * myVar; – pointeur non qualifié vers le type d’object qualifié const

      • MyObj * const myVar; – pointeur qualifié de const vers un type d’object non qualifié

    • Utilisation la plus à gauche – appliquée au type d’object, pas variable

      • const MyObj * myVar; – pointeur non qualifié vers le type d’object qualifié const

AINSI:

static NSSsortingng * const myVar; – pointeur constant sur une chaîne immuable avec un lien interne.

L’absence du mot-clé static rend le nom de variable global et peut entraîner des conflits de noms au sein de l’application.