declarações de variáveis ??em arquivos de cabeçalho - estática ou não?
Pergunta
Quando refatoração afastado alguns #defines
me deparei com declarações semelhantes ao seguinte em um C ++ arquivo de cabeçalho:
static const unsigned int VAL = 42;
const unsigned int ANOTHER_VAL = 37;
A questão é: que diferença, se houver, será a marca estática? Note-se que múltiplos inclusão dos cabeçalhos não é possível devido ao truque #ifndef HEADER
clássico #define HEADER
#endif
(se o que importa).
O estática significa apenas uma cópia de VAL
é criado, caso o cabeçalho é incluído por mais de um arquivo de origem?
Solução
Os meios static
que haverá uma cópia do VAL
criado para cada arquivo-fonte está incluído. Mas isso também significa que várias inclusões não vai resultar em múltiplas definições de VAL
que irá colidir em tempo de ligação. Em C, sem a static
você precisa garantir que definiu arquivo VAL
apenas uma fonte, enquanto os outros arquivos de origem declarou extern
. Normalmente, um faria isso, definindo-o (possivelmente com um inicializador) em um arquivo de origem e colocar a declaração extern
em um arquivo de cabeçalho.
variáveis ??static
a nível global são visíveis apenas em seu próprio arquivo de origem se eles chegaram lá através de uma incluir ou estavam no arquivo principal.
Nota do editor:. Em C ++, const
objetos nem com os static
nem extern
palavras-chave em sua declaração são implicitamente static
Outras dicas
As tags static
e extern
sobre variáveis ??com escopo de arquivo determinar se eles são acessíveis em outras unidades de tradução (ou seja, outros .c
ou .cpp
arquivos).
-
static
dá a ligação interna variável, escondendo-lo de outras unidades de tradução. No entanto, as variáveis ??com ligação interna pode ser definida de várias unidades de tradução. -
extern
dá a ligação externa variável, tornando-o visível para outras unidades de tradução. Normalmente, isso significa que a única variável devem ser definidos em uma unidade de tradução.
O padrão (quando você não especificar static
ou extern
) é uma daquelas áreas em que C e C ++ diferem.
-
Em C, as variáveis ??com escopo de arquivo são
extern
(ligação externa) por padrão. Se você estiver usando C,VAL
éstatic
eANOTHER_VAL
éextern
. -
Em C ++, variáveis ??com escopo de arquivo são
static
(ligação interna) por padrão se foremconst
eextern
por padrão, se eles não são. Se você estiver usando C ++, tantoVAL
eANOTHER_VAL
sãostatic
.
A partir de um projecto do C especificação :
6.2.2 Ligações de identificadores ... -5- Se a declaração de um identificador para uma função não tem especificador de classe de armazenamento, sua ligação é determinado exatamente como se fosse declarado com o especificador externa de armazenamento de classe. E se a declaração de um identificador para um objeto tem escopo do arquivo e nenhum especificador de classe de armazenamento, sua ligação é externo.
A partir de um projecto do C ++ especificação :
7.1.1 - especificadores de classe de armazenamento [dcl.stc] ... -6- Um nome declarado em um escopo namespace sem um armazenamento de classe-especificador tem ligação externa a menos que tenha ligação interna por causa de uma declaração anterior e desde que não seja declarada const. Objectos declarados const e não explicitamente declarado extern tem ligação interna.
O estática significa que você vai obter uma cópia por arquivo, mas ao contrário de outros disseram que é perfeitamente legal para fazê-lo. Você pode facilmente testar isso com um exemplo de código pequeno:
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;
}
A execução deste lhe dá essa saída:
0x446020
0x446040
variáveis ??const
em C ++ têm ligação interna. Assim, usando static
não tem efeito.
A.H.
const int i = 10;
one.cpp
#include "a.h"
func()
{
cout << i;
}
two.cpp
#include "a.h"
func1()
{
cout << i;
}
Se isto fosse um programa C, você teria de erro 'múltiplo definição' para i
(devido à ligação externa).
A declaração estática neste nível de meios de código que o variabel só é visível na unidade de compilação atual. Isto significa que somente o código dentro desse módulo vai ver essa variável.
Se você tem um arquivo de cabeçalho que declara uma variável estática e que cabeçalho está incluído em múltiplos C / arquivos CPP, em seguida, essa variável será "local" a esses módulos. Haverá N cópias desse variável para os locais de N que cabeçalho é incluído. Eles não estão relacionados uns aos outros em tudo. Qualquer código dentro de qualquer um desses arquivos de origem só irá referenciar a variável que é declarada dentro desse módulo.
Neste caso particular, a palavra-chave 'estática' não parece estar fornecendo qualquer benefício. Eu poderia estar faltando alguma coisa, mas parece que não importa -. Eu nunca vi qualquer coisa como isto antes
Quanto inlining, neste caso a variável é provável embutido, mas isso é só porque ele é declarado const. O compilador força ser mais propensos a variáveis ??módulo estáticos em linha, mas é dependente da situação e do código que está sendo compilado. Não há garantia de que o compilador irá inline 'estática'.
O livro C (free on-line) tem um capítulo sobre ligação, o que explica o significado de 'estático' em mais detalhes (embora a resposta correta já é dado em outros comentários): http://publications.gbdirect.co.uk/c_book/chapter4/linkage. html
Para responder à pergunta, "é que a estática significa apenas uma cópia do VAL é criado, caso o cabeçalho é incluído por mais de um arquivo de origem?" ...
NÃO . VAL será sempre definido separadamente em cada arquivo que inclui o cabeçalho.
As normas para C e C ++ fazer causa uma diferença neste caso.
Em C, as variáveis ??com escopo de arquivo são extern por padrão. Se você estiver usando C, VAL é estático e ANOTHER_VAL é externa.
Note que os ligantes modernos podem reclamar ANOTHER_VAL se o cabeçalho está incluído em arquivos diferentes (mesmo nome global definida duas vezes), e definitivamente reclamar se ANOTHER_VAL foi inicializado com um valor diferente em outro arquivo
Em C ++, variáveis ??com escopo de arquivo são estáticos por padrão, se eles são const e externo por padrão, se eles não são. Se você estiver usando C ++, tanto VAL e ANOTHER_VAL são estáticos.
Você também precisa levar em conta o fato de que ambas as variáveis ??são designadas const. Idealmente o compilador sempre escolher para inline essas variáveis ??e não incluem qualquer armazenamento para eles. Há toda uma série de razões pelas quais o armazenamento podem ser alocados. Que eu posso pensar ...
- opções de depuração
- endereço tomadas no arquivo
- compilador sempre aloca armazenamento (tipos const complexos não pode facilmente ser embutido, então torna-se um caso especial para tipos básicos)
Você não pode declarar uma variável estática sem defini-lo bem (isto é porque o armazenamento modificadores de classe estática e externa são mutuamente exclusivos). Uma variável estática pode ser definida em um arquivo de cabeçalho, mas isso faria com que cada arquivo-fonte que incluiu o arquivo de cabeçalho para ter a sua própria cópia privada da variável, o que provavelmente não é o que se pretendia.
Assumindo que essas declarações estão no escopo global (ou seja, não são variáveis ??de membro), então:
estática 'ligação interna' significa. Neste caso, uma vez que é declarado const isso pode ser otimizado / inlined pelo compilador. Se você omitir o const seguida, o compilador deve alocar armazenamento em cada unidade de compilação.
Ao omitir estática a ligação é externo por padrão. Mais uma vez, você foi salvo pela const ness - o compilador pode otimizar o uso de / em linha. Se deixar cair o const , então você vai ter um símbolos multiplicar definidos Erro em tempo de ligação.
const variáveis ??são de estática padrão em C ++, mas extern C. Então, se você usar C ++ isso não faz sentido que a construção para uso.
(7.11.6 C ++ 2003, e Apexndix C tem amostras)
Exemplo de compilação comparar / fontes destino como programa de 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
estática impede outra unidade de compilação de externing essa variável para que o compilador pode apenas "inline" o valor da variável onde ele é usado e não criar armazenamento de memória para ele.
Em seu segundo exemplo, o compilador não pode assumir que algum outro arquivo de origem não extern-lo, por isso deve realmente armazenar esse valor em algum lugar da memória.
estática impede o compilador de adicionar várias instâncias. Isto torna-se menos importante com a proteção #ifndef, mas supondo que o cabeçalho está incluído em duas bibliotecas separadas, e a aplicação está ligada, duas instâncias seria incluído.