93 votes

Déclarations de variables dans les fichiers d'en-tête - statiques ou non ?

Lors de la refactorisation de certains #defines Je suis tombé sur des déclarations similaires à la suivante dans un fichier d'en-tête C++ :

static const unsigned int VAL = 42;
const unsigned int ANOTHER_VAL = 37;

La question est de savoir quelle différence, le cas échéant, fera la statique. Notez que l'inclusion multiple des en-têtes n'est pas possible en raison de la classique #ifndef HEADER #define HEADER #endif (si cela compte).

Est-ce que le statique signifie qu'une seule copie de VAL est créé, dans le cas où l'en-tête est inclus par plus d'un fichier source ?

0 votes

112voto

bk1e Points 13737

Le site static et extern sur les variables à l'échelle du fichier déterminent si elles sont accessibles dans d'autres unités de traduction (c'est-à-dire, d'autres .c ou .cpp ).

  • static donne à la variable un lien interne, la dissimulant aux autres unités de traduction. Toutefois, les variables ayant un lien interne peuvent être définies dans plusieurs unités de traduction.

  • extern donne à la variable un lien externe, ce qui la rend visible pour les autres unités de traduction. En général, cela signifie que la variable ne doit être définie que dans une seule unité de traduction.

La valeur par défaut (lorsque vous ne spécifiez pas static ou extern ) est l'un des domaines dans lesquels le C et le C++ diffèrent.

  • En C, les variables de type fichier sont extern (lien externe) par défaut. Si vous utilisez C, VAL est static et ANOTHER_VAL est extern .

  • En C++, les variables à portée de fichier sont static (lien interne) par défaut s'ils sont const et extern par défaut s'ils ne le sont pas. Si vous utilisez C++, les deux VAL et ANOTHER_VAL sont static .

D'après une ébauche du Spécification C :

6.2.2 Liens entre les identifiants ... -5- Si la déclaration d'un identificateur de fonction n'a pas de spécificateur de classe de stockage, son lien est déterminé exactement comme s'il était déclaré avec le spécificateur de classe de stockage extern. est déterminé exactement comme s'il était déclaré avec le spécificateur de classe de stockage extern. Si la déclaration de l'identificateur d'un objet a une portée de fichier et pas de spécificateur de classe de stockage, son lien est externe.

D'après une ébauche du Spécification C++ :

7.1.1 - Spécification des classes de stockage [dcl.stc]. ... -6- Un nom déclaré dans un espace de nom sans spécificateur de classe de stockage a un lien externe à moins qu'il n'ait un lien interne à cause d'une déclaration précédente et à condition qu'il ne soit pas déclaré const. Les objets déclarés const et non déclarés explicitement extern ont un lien interne.

108voto

Justsalt Points 1083

Le site static signifie qu'il y aura une copie de VAL créé pour chaque fichier source dans lequel il est inclus. Mais cela signifie également que des inclusions multiples ne donneront pas lieu à de multiples définitions de VAL qui entreront en collision au moment du lien. En C, sans le static vous devrez vous assurer qu'un seul fichier source défini VAL alors que les autres fichiers sources l'ont déclaré extern . Habituellement, on le fait en le définissant (éventuellement avec un initialisateur) dans un fichier source et en plaçant la balise extern dans un fichier d'en-tête.

static Les variables au niveau global ne sont visibles que dans leur propre fichier source, qu'elles y soient arrivées via un include ou qu'elles se trouvent dans le fichier principal.


Note de l'éditeur : En C++, const avec ni le static ni extern dans leur déclaration sont implicitement static .

0 votes

Je suis un fan de la dernière phrase, incroyablement utile. Je n'ai pas voté pour la réponse parce que la 42 est meilleure. edit : grammar

0 votes

"Le statique signifie qu'il y aura une copie de VAL créée pour chaque fichier source dans lequel il est inclus." Cela semble impliquer qu'il y aurait deux copies de VAL si deux fichiers sources incluaient le fichier d'en-tête. J'espère que ce n'est pas vrai, et qu'il y a toujours une seule instance de VAL, quel que soit le nombre de fichiers incluant l'en-tête.

4 votes

@Brent212 Le compilateur ne sait pas si une déclaration/définition provient d'un fichier d'en-tête ou du fichier principal. Vous espérez donc en vain. Il y aura deux copies de VAL si quelqu'un a été stupide et a mis une définition statique dans un fichier d'en-tête et qu'elle a été incluse dans deux sources.

48voto

slicedlime Points 1260

Le statique signifie que vous obtiendrez une copie par fichier, mais contrairement à ce que d'autres ont dit, il est parfaitement légal de le faire. Vous pouvez facilement tester cela avec un petit échantillon de code :

test.h :

static int TEST = 0;
void test();

test1.cpp :

#include <iostream>
#include "test.h"

int main(void) {
    std::cout << &TEST << std::endl;
    test();
}

test2.cpp :

#include <iostream>
#include "test.h"

void test() {
    std::cout << &TEST << std::endl;
}

En exécutant ceci, vous obtenez ce résultat :

0x446020
0x446040

5 votes

Merci pour l'exemple !

0 votes

Je me demande si TEST étaient const si LTO était capable de l'optimiser en un seul emplacement mémoire. Mais -O3 -flto de GCC 8.1 ne l'a pas fait.

0 votes

Il serait illégal pour lui de le faire - même si c'est une constante, la statique garantit que chaque instance est locale à l'unité de compilation. Il pourrait probablement mettre en ligne la valeur de la constante elle-même si elle est utilisée comme une constante, mais puisque nous prenons son adresse, il doit retourner un pointeur unique.

6voto

Nitin Points 21

const Les variables en C++ ont un lien interne. Ainsi, en utilisant static n'a aucun effet.

a.h

const int i = 10;

one.cpp

#include "a.h"

func()
{
   cout << i;
}

deux.cpp

#include "a.h"

func1()
{
   cout << i;
}

S'il s'agissait d'un programme C, vous obtiendriez l'erreur 'définition multiple' pour i (en raison d'un lien externe).

5voto

Mark Points 6505

La déclaration statique à ce niveau du code signifie que la variable n'est visible que dans l'unité de compilation actuelle. Cela signifie que seul le code de ce module verra cette variable.

si vous avez un fichier d'en-tête qui déclare une variable statique et que cet en-tête est inclus dans plusieurs fichiers C/CPP, alors cette variable sera "locale" à ces modules. Il y aura N copies de cette variable pour les N endroits où cet en-tête est inclus. Elles ne sont pas du tout liées les unes aux autres. Tout code contenu dans l'un de ces fichiers source ne fera référence qu'à la variable déclarée dans ce module.

Dans ce cas particulier, le mot clé "static" ne semble pas apporter d'avantage. Il se peut que quelque chose m'échappe, mais il semble que cela n'ait pas d'importance - je n'ai jamais rien vu de tel auparavant.

Quant à l'inlining, dans ce cas la variable est probablement inliné, mais c'est seulement parce qu'elle est déclarée const. Le compilateur pourrait sera plus enclin à mettre en ligne les variables statiques des modules, mais cela dépend de la situation et du code compilé. Il n'y a aucune garantie que le compilateur mettra en ligne les "statics".

0 votes

L'avantage de "static" ici est qu'autrement vous déclarez plusieurs globaux avec le même nom, un pour chaque module qui inclut l'en-tête. Si l'éditeur de liens ne se plaint pas, c'est seulement parce qu'il mord sa langue et est poli.

0 votes

Dans ce cas, en raison de la const le static est implicite et donc facultative. Le corollaire est qu'il n'y a pas de risque d'erreurs de définition multiples comme le prétendait Mike F.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X