it-swarm.it

Dichiarazioni variabili nei file di intestazione: statiche o no?

Quando refactoring via alcuni #defines Mi sono imbattuto in dichiarazioni simili alle seguenti in un file di intestazione C++:

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

La domanda è: quale differenza farà l'eventuale statica? Tieni presente che l'inclusione multipla delle intestazioni non è possibile a causa del classico #ifndef HEADER#define HEADER#endif trucco (se è importante).

Lo statico significa che viene creata una sola copia di VAL, nel caso in cui l'intestazione sia inclusa da più di un file sorgente?

86
Rob

static indica che verrà creata una copia di VAL per ogni file di origine in cui è incluso. Ma significa anche che inclusioni multiple non comporteranno definizioni multiple di VAL che si scontrerà al momento del collegamento. In C, senza static dovresti assicurarti che solo un file sorgente abbia definito VAL mentre gli altri file sorgente lo hanno dichiarato extern. Di solito uno lo farebbe definendolo (possibilmente con un inizializzatore) in un file sorgente e inserendo la dichiarazione extern in un file di intestazione.

static Le variabili a livello globale sono visibili nel proprio file sorgente solo se sono arrivate lì tramite un'inclusione o erano nel file principale.


Nota del redattore: In C++, gli oggetti const senza le parole chiave staticextern nella loro dichiarazione sono implicitamente static.

102
Justsalt

I tag static e extern sulle variabili con ambito file determinano se sono accessibili in altre unità di traduzione (ovvero altri .c o .cpp File).

  • static fornisce il collegamento interno variabile, nascondendolo da altre unità di traduzione. Tuttavia, le variabili con collegamento interno possono essere definite in più unità di traduzione.

  • extern fornisce il collegamento esterno variabile, rendendolo visibile ad altre unità di traduzione. In genere ciò significa che la variabile deve essere definita in una sola unità di traduzione.

L'impostazione predefinita (quando non si specifica static o extern) è una di quelle aree in cui C e C++ differiscono.

  • In C, le variabili con ambito file sono extern (collegamento esterno) per impostazione predefinita. Se stai usando C, VAL è static e ANOTHER_VAL è extern.

  • In C++, le variabili con ambito file sono static (collegamento interno) per impostazione predefinita se sono const e extern per impostazione predefinita se non lo sono. Se stai usando C++, sia VAL che ANOTHER_VAL sono static.

Da una bozza del specifica C :

6.2.2 Collegamenti di identificatori ... -5- Se la dichiarazione di un identificatore per una funzione non ha un identificatore della classe di archiviazione, il suo collegamento viene determinato esattamente come se fosse dichiarato con l'identificatore della classe di archiviazione esterno. Se la dichiarazione di un identificatore per un oggetto ha un ambito file e nessun identificatore della classe di archiviazione, il suo collegamento è esterno.

Da una bozza del specifica C++ :

7.1.1 - Identificatori della classe di archiviazione [dcl.stc] ... -6- Un nome dichiarato in un ambito dello spazio dei nomi senza un identificatore della classe di archiviazione ha un collegamento esterno a meno che non abbia un collegamento interno a causa di una dichiarazione precedente e purché non lo sia const dichiarata Gli oggetti dichiarati const e non esplicitamente dichiarati extern hanno un collegamento interno.

107
bk1e

La statica significherà che otterrai una copia per file, ma a differenza di altri hanno detto che è perfettamente legale farlo. Puoi facilmente testarlo con un piccolo esempio di codice:

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;
}

L'esecuzione di questo ti dà questo output:

0x446020
0x446040

46
slicedlime

const le variabili in C++ hanno un collegamento interno. Quindi, l'uso di static non ha alcun effetto.

a.h

const int i = 10;

one.cpp

#include "a.h"

func()
{
   cout << i;
}

two.cpp

#include "a.h"

func1()
{
   cout << i;
}

Se si trattasse di un programma C, si otterrebbe l'errore di "definizione multipla" per i (a causa del collegamento esterno).

6
Nitin

La dichiarazione statica a questo livello di codice indica che la variabel è visibile solo nell'unità di compilazione corrente. Ciò significa che solo il codice all'interno di quel modulo vedrà quella variabile.

se si dispone di un file di intestazione che dichiara una variabile statica e tale intestazione è inclusa in più file C/CPP, tale variabile sarà "locale" per tali moduli. Ci saranno N copie di quella variabile per le N posizioni in cui è inclusa l'intestazione. Non sono affatto collegati tra loro. Qualsiasi codice all'interno di uno di questi file di origine farà riferimento solo alla variabile dichiarata all'interno di quel modulo.

In questo caso particolare, la parola chiave "statica" non sembra fornire alcun vantaggio. Potrei mancare qualcosa, ma sembra non avere importanza: non ho mai visto nulla di simile prima d'ora.

Per quanto riguarda l'inline, in questo caso la variabile è probabilmente inline, ma è solo perché è dichiarata const. Il compilatore potrebbe ha maggiori probabilità di incorporare le variabili statiche del modulo, ma ciò dipende dalla situazione e dal codice che viene compilato. Non vi è alcuna garanzia che il compilatore includa "statica".

5
Mark

Per rispondere alla domanda "lo statico significa che viene creata una sola copia di VAL, nel caso in cui l'intestazione sia inclusa da più di un file sorgente?" ...

[~ ~ #] non [~ ~ #]. VAL sarà sempre definito separatamente in ogni file che include l'intestazione.

Gli standard per C e C++ fanno la differenza in questo caso.

In C, le variabili con ambito file sono esterne per impostazione predefinita. Se stai usando C, VAL è statico e ANOTHER_VAL è esterno.

Si noti che i linker moderni potrebbero lamentarsi di ANOTHER_VAL se l'intestazione è inclusa in file diversi (stesso nome globale definito due volte) e si lamenterebbero sicuramente se ANOTHER_VAL fosse inizializzato su un valore diverso in un altro file

In C++, le variabili con ambito file sono statiche per impostazione predefinita se sono const ed extern per impostazione predefinita se non lo sono. Se stai usando C++, sia VAL che ANOTHER_VAL sono statici.

È inoltre necessario tenere conto del fatto che entrambe le variabili sono designate const. Idealmente, il compilatore sceglierebbe sempre di incorporare queste variabili e di non includere alcuna memoria per esse. Esistono molti motivi per cui è possibile allocare memoria. Quelli a cui riesco a pensare ...

  • opzioni di debug
  • indirizzo preso nel file
  • il compilatore alloca sempre l'archiviazione (i tipi di const complessi non possono essere facilmente incorporati, quindi diventa un caso speciale per i tipi di base)
2
itj

Il libro C (gratuito online) ha un capitolo sul collegamento, che spiega più dettagliatamente il significato di "statico" (sebbene la risposta corretta sia già fornita in altri commenti): http://publications.gbdirect.co .uk/c_book/Capitolo 4/linkage.html

2
Jan de Vos

Non puoi dichiarare una variabile statica anche senza definirla (questo perché i modificatori della classe di archiviazione statici ed esterni si escludono a vicenda). Una variabile statica può essere definita in un file di intestazione, ma ciò farebbe sì che ogni file di origine che includeva il file di intestazione avesse la propria copia privata della variabile, che probabilmente non è ciò che era previsto.

2
Gajendra Kumar

Supponendo che queste dichiarazioni abbiano portata globale (ovvero non siano variabili membro), quindi:

statico significa "collegamento interno". In questo caso, poiché è dichiarato const questo può essere ottimizzato/integrato dal compilatore. Se si omette const, il compilatore deve allocare memoria in ciascuna unità di compilazione.

Omettendo statico il collegamento è esterno per impostazione predefinita. Ancora una volta, sei stato salvato da const ness - il compilatore può ottimizzare/utilizzare in linea. Se si rilascia const, si otterrà un errore moltiplicato per i simboli al momento del collegamento.

1
Seb Rose

const le variabili sono di default statiche in C++, ma esternamente C. Quindi se usi C++ questo non ha senso quale costruzione usare.

(7.11.6 C++ 2003 e Apexndix C ha degli esempi)

Esempio nel confrontare le fonti di compilazione/collegamento come programma C e 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

Statico impedisce a un'altra unità di compilazione di esternare quella variabile in modo che il compilatore possa semplicemente "incorporare" il valore della variabile in cui viene utilizzata e non creare memoria per essa.

Nel tuo secondo esempio, il compilatore non può presumere che qualche altro file sorgente non lo esternerà, quindi deve effettivamente archiviare quel valore in memoria da qualche parte.

0
Jim Buck