Domanda

Quando si esegue il refactoring, 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, se esiste, farà la 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 solo una copia di VAL viene creato, nel caso in cui l'intestazione sia inclusa in più di un file sorgente?

È stato utile?

Soluzione

IL static significa che ci sarà una copia di VAL creato per ogni file sorgente in cui è incluso.Ma significa anche che più inclusioni non daranno luogo a più definizioni di VAL che si scontreranno al momento del collegamento.In C, senza il static dovresti assicurarti che sia definito un solo file sorgente VAL mentre gli altri file sorgente lo hanno dichiarato extern.Di solito lo si fa definendolo (possibilmente con un inizializzatore) in un file sorgente e inserendo il file extern dichiarazione in un file di intestazione.

static le variabili a livello globale sono visibili solo nel proprio file sorgente sia che siano arrivate tramite un'inclusione o che si trovassero nel file principale.


Nota dell'editore: Nel C++, const oggetti senza né il staticextern le parole chiave nella loro dichiarazione sono implicitamente static.

Altri suggerimenti

IL static E extern i tag sulle variabili con ambito file determinano se sono accessibili in altre unità di traduzione (ad es.altro .c O .cpp File).

  • static fornisce il collegamento interno variabile, nascondendolo alle 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 solo in un'unità di traduzione.

Il valore predefinito (quando non lo specifichi 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 usi C, VAL È static E ANOTHER_VAL È extern.

  • In C++, le variabili con ambito file sono static (collegamento interno) per impostazione predefinita, se lo sono const, E extern per impostazione predefinita se non lo sono.Se usi C++, entrambi VAL E ANOTHER_VAL Sono static.

Da una bozza del specifica C:

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

Da una bozza del Specifica C++:

7.1.1 - Specificatori della classe di archiviazione [dcl.stc] ...-6- Un nome dichiarato nell'ambito di uno spazio dei nomi senza uno specificatore 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 sia dichiarato const.Gli oggetti dichiarati const e non esplicitamente dichiarati extern hanno un collegamento interno.

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

prova.h:

static int TEST = 0;
void test();

prova1.cpp:

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

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

prova2.cpp:

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

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

L'esecuzione di questo ti dà questo output:

0x446020
0x446040

const le variabili in C++ hanno un collegamento interno.Quindi, usando static non ha alcun effetto.

a.h

const int i = 10;

one.cpp

#include "a.h"

func()
{
   cout << i;
}

due.cpp

#include "a.h"

func1()
{
   cout << i;
}

Se questo fosse un programma C, riceverai l'errore "definizione multipla" per i (a causa del collegamento esterno).

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

se hai un file di intestazione che dichiara una variabile statica e quell'intestazione è inclusa in più file C/CPP, quella variabile sarà "locale" per quei moduli.Ci saranno N copie di quella variabile per gli N posti in cui è inclusa l'intestazione.Non sono affatto imparentati tra loro.Qualsiasi codice all'interno di uno qualsiasi di questi file sorgente farà riferimento solo alla variabile dichiarata all'interno di quel modulo.

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

Per quanto riguarda l'inlining, in questo caso la variabile è probabilmente inline, ma solo perché è dichiarata const.Il compilatore Potrebbe essere più propenso a inline variabili statiche del modulo, ma ciò dipende dalla situazione e dal codice in fase di compilazione.Non vi è alcuna garanzia che il compilatore incorpori "statici" inline.

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

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

NO.VAL sarà sempre definito separatamente in ogni file che include l'intestazione.

In questo caso gli standard per C e C++ causano una differenza.

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

Tieni presente 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 utilizzi 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 memorizzazione per esse.Esistono numerosi motivi per cui è possibile allocare lo spazio di archiviazione.Quelli che mi vengono in mente...

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

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

Supponendo che queste dichiarazioni abbiano portata globale (cioènon sono variabili membro), quindi:

statico significa "collegamento interno".In questo caso, poiché è dichiarato cost questo può essere ottimizzato/incorporato dal compilatore.Se ometti il cost quindi il compilatore deve allocare spazio di archiviazione in ciascuna unità di compilazione.

Omettendo statico il collegamento è esterno per impostazione predefinita.Ancora una volta, sei stato salvato da costness: il compilatore può ottimizzare/inline l'utilizzo.Se lasci cadere il cost allora otterrai un moltiplicare i simboli definiti errore al momento del collegamento.

cost le variabili sono statiche per impostazione predefinita in C++, ma extern C.Quindi se usi C++ non ha senso quale costruzione usare.

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

Esempio di confronto di sorgenti di compilazione/collegamento come programmi 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

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

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

Static impedisce al compilatore di aggiungere più istanze.Ciò diventa meno importante con la protezione #ifndef, ma presupponendo che l'intestazione sia inclusa in due librerie separate e che l'applicazione sia collegata, verrebbero incluse due istanze.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top