web-dev-qa-db-fra.com

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 à celles-ci 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, le statique fera-t-il? Notez que l'inclusion multiple des en-têtes n'est pas possible à cause du classique #ifndef HEADER#define HEADER#endif astuce (si cela compte).

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

86
Rob

static signifie qu’une copie de VAL sera créée pour chaque fichier source dans lequel elle est incluse. Cela signifie également que plusieurs inclusions ne donneront pas lieu à plusieurs définitions de VAL qui se heurtera au moment du lien. En C, sans le static, vous devez vous assurer que seul un fichier source définit VAL, tandis que les autres fichiers source le déclarent extern. Cela se fait généralement en le définissant (éventuellement avec un initialiseur) dans un fichier source et en plaçant la déclaration extern dans un fichier d'en-tête.

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


Note de la rédaction: En C++, les objets const ne contenant ni les mots-clés static ni extern dans leur déclaration sont implicitement static.

102
Justsalt

Les balises static et extern des variables à portée de fichier déterminent si elles sont accessibles dans d’autres unités de traduction (c.-à-d. D’autres .c ou .cpp des dossiers).

  • static donne le lien interne variable en le cachant des autres unités de traduction. Toutefois, les variables avec une liaison interne peuvent être définies dans plusieurs unités de traduction.

  • extern donne le lien externe variable, le rendant visible par les autres unités de traduction. Cela signifie généralement 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 C et C++ diffèrent.

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

  • En C++, les variables de fichier couvertes sont static (liaison interne) par défaut, si elles sont const, et extern par défaut si elles ne le sont pas. Si vous utilisez C++, VAL et ANOTHER_VAL sont static.

À partir d'un brouillon de spécification C :

6.2.2 Liaisons d'identifiants ... -5- Si la déclaration d'un identifiant pour une fonction n'a pas de spécificateur de classe de stockage, sa liaison est déterminée exactement comme si elle avait été déclarée avec le spécificateur de classe de stockage extern. Si la déclaration d'un identifiant pour un objet a une portée de fichier et aucun spécificateur de classe de stockage, sa liaison est externe.

À partir d'un brouillon de spécification C++ :

7.1.1 - Spécificateurs de classe de stockage [dcl.stc] ... -6- Un nom déclaré dans une portée d'espace de noms sans spécificateur de classe de stockage a une liaison externe sauf s'il possède une liaison interne en raison d'une déclaration précédente et à condition que ce ne soit pas le cas. déclaré const. Les objets déclarés const et non explicitement déclarés extern ont une liaison interne.

107
bk1e

La statique signifie que vous obtenez une copie par fichier, mais contrairement à d'autres, il est parfaitement légal de le faire. Vous pouvez facilement tester cela avec un petit exemple 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 cours d'exécution, vous obtenez cette sortie:

0x446020
0x446040

46
slicedlime

const les variables en C++ ont un lien interne. Donc, utiliser 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 en C, vous obtiendrez l'erreur 'Plusieurs définitions' pour i (en raison d'un lien externe).

6
Nitin

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

si vous avez un fichier en-tête qui déclare une variable statique et que cet en-tête est inclus dans plusieurs fichiers C/CPP, cette variable sera "locale" pour ces modules. Il y aura N copies de cette variable pour les N places où cet en-tête est inclus. Ils ne sont pas liés les uns 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é "statique" ne semble pas offrir d'avantage. Il se peut que je manque quelque chose, mais cela ne semble pas avoir d'importance - je n'avais jamais rien vu de tel.

En ce qui concerne l'inline, dans ce cas, la variable est probablement en ligne, mais c'est uniquement parce qu'elle est déclarée const. Le compilateur peut-être sera plus enclin à intégrer des variables statiques de module, mais cela dépend de la situation et du code en cours de compilation. Il n'y a aucune garantie que le compilateur incorporera la "statique".

5
Mark

Pour répondre à la question, "le terme statique signifie-t-il qu'une seule copie de VAL est créée, si l'en-tête est inclus par plusieurs fichiers source?" ...

NO . VAL sera toujours défini séparément dans chaque fichier incluant l'en-tête.

Les normes pour C et C++ causent une différence dans ce cas.

En C, les variables de type fichier sont externes par défaut. Si vous utilisez C, VAL est statique et ANOTHER_VAL est extern.

Notez que les lieurs modernes peuvent se plaindre d'ANOTHER_VAL si l'en-tête est inclus dans différents fichiers (même nom global défini deux fois), et se plaindraient certainement si ANOTHER_VAL était initialisé à une valeur différente dans un autre fichier.

En C++, les variables de fichier sont définies par défaut si elles sont const et externes par défaut si elles ne le sont pas. Si vous utilisez C++, VAL et ANOTHER_VAL sont statiques.

Vous devez également tenir compte du fait que les deux variables sont désignées par const. Dans l’idéal, le compilateur choisirait toujours d’inscrire ces variables en ligne et de ne pas inclure de stockage pour celles-ci. Il existe une foule de raisons pour lesquelles le stockage peut être alloué. Ceux auxquels je peux penser ...

  • options de débogage
  • adresse prise dans le dossier
  • le compilateur alloue toujours le stockage (les types const complexes ne peuvent pas être facilement alignés, ce qui en fait un cas particulier pour les types de base)
2
itj

Le livre C (gratuit en ligne) contient un chapitre sur les liens, qui explique plus en détail la signification du terme "statique" (bien que la réponse correcte soit déjà donnée dans d'autres commentaires): http://publications.gbdirect.co .uk/c_book/chapter4/linkage.html

2
Jan de Vos

Vous ne pouvez pas déclarer une variable statique sans la définir également (en effet, les modificateurs de classe de stockage static et extern s’excluent mutuellement). Une variable statique peut être définie dans un fichier d’en-tête, mais cela ferait que chaque fichier source incluant le fichier d’en-tête possède sa propre copie privée de la variable, ce qui n’est probablement pas ce qui était prévu.

2
Gajendra Kumar

En supposant que ces déclarations ont une portée globale (c’est-à-dire ne sont pas des variables membres), alors:

statique signifie "liaison interne". Dans ce cas, puisqu'il est déclaré const cela peut être optimisé/en ligne par le compilateur. Si vous omettez le const, le compilateur doit allouer de la mémoire dans chaque unité de compilation.

En omettant statique, le lien est extern par défaut. Encore une fois, vous avez été enregistré par const ness - le compilateur peut optimiser/utiliser en ligne. Si vous supprimez le const, vous obtiendrez une erreur en multipliant les symboles définis au moment du lien.

1
Seb Rose

Les variables const sont par défaut statiques en C++, mais en externe C. Donc, si vous utilisez C++, cela n'a aucun sens de savoir quelle construction utiliser.

(7.11.6 C++ 2003, et Apexndix C a des exemples)

Exemple de comparaison des sources de compilation/liaison en tant que programme C et C++:

bruziuz:~/test$ cat a.c
const int b = 22;
int main(){return 0;}
bruziuz:~/test$ cat b.c
const int b=2;
bruziuz:~/test$ gcc -x c -std=c89 a.c b.c
/tmp/ccSKKIRZ.o:(.rodata+0x0): multiple definition of `b'
/tmp/ccDSd0V3.o:(.rodata+0x0): first defined here
collect2: error: ld returned 1 exit status
bruziuz:~/test$ gcc -x c++ -std=c++03 a.c b.c 
bruziuz:~/test$ 
bruziuz:~/test$ gcc --version | head -n1
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609
1
bruziuz

Static empêche une autre unité de compilation d'externer cette variable, de sorte que le compilateur peut simplement "insérer" la valeur de la variable là où elle est utilisée et ne pas créer de mémoire pour celle-ci.

Dans votre deuxième exemple, le compilateur ne peut pas supposer qu'un autre fichier source ne l'externera pas. Il doit donc stocker cette valeur en mémoire quelque part.

0
Jim Buck