Ao refatorar alguns #defines
, encontrei declarações semelhantes às seguintes em um arquivo de cabeçalho C ++:
static const unsigned int VAL = 42;
const unsigned int ANOTHER_VAL = 37;
A questão é: que diferença, se houver, a estática fará? Observe que a inclusão múltipla dos cabeçalhos não é possível devido ao #ifndef HEADER
#define HEADER
#endif
truque clássico (se isso for importante).
O estático significa que apenas uma cópia do VAL
é criada, caso o cabeçalho seja incluído por mais de um arquivo de origem?
Respostas:
Os
static
meios de que haverá uma cópia doVAL
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 deVAL
que irá colidir em tempo de ligação. Em C, sem o,static
você precisaria garantir que apenas um arquivo de origem foi definidoVAL
enquanto os outros arquivos de origem o declararamextern
. Normalmente, isso seria feito definindo-o (possivelmente com um inicializador) em um arquivo de origem e colocando oextern
declaração em um arquivo de cabeçalho.static
variáveis em nível global só são visíveis em seu próprio arquivo de origem, quer tenham chegado lá por meio de uma inclusão ou estivessem no arquivo principal.Nota do editor: Em C ++, os
const
objetos sem as palavras-chavestatic
nemextern
em suas declarações são implicitamentestatic
.fonte
As tags
static
eextern
nas variáveis com escopo de arquivo determinam se elas são acessíveis em outras unidades de tradução (isto é, outros.c
ou.cpp
arquivos).static
dá a ligação interna variável, ocultando-a de outras unidades de tradução. No entanto, as variáveis com ligação interna podem ser definidas em várias unidades de tradução.extern
dá a ligação externa variável, tornando-a visível para outras unidades de tradução. Normalmente, isso significa que a variável só deve ser definida em uma unidade de tradução.O padrão (quando você não especifica
static
ouextern
) é 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 ++, as variáveis com escopo de arquivo são
static
(ligação interna) por padrão se foremconst
, eextern
por padrão se não forem. Se você estiver usando C ++, ambosVAL
eANOTHER_VAL
estãostatic
.De um esboço da especificação C :
De um rascunho da especificação C ++ :
fonte
A estática significa que você terá uma cópia por arquivo, mas ao contrário de outros disseram, é perfeitamente legal fazer isso. Você pode testar isso facilmente com um pequeno exemplo de código:
test.h:
test1.cpp:
test2.cpp:
Executá-lo fornece esta saída:
fonte
TEST
fosseconst
, se o LTO seria capaz de otimizá-lo em um único local de memória. Mas-O3 -flto
do GCC 8.1 não.const
variáveis em C ++ têm ligação interna. Portanto, usarstatic
não tem efeito.ah
one.cpp
two.cpp
Se este fosse um programa C, você obteria o erro de 'definição múltipla' para
i
(devido à ligação externa).fonte
static
tem o efeito de sinalizar nitidamente a intenção e a consciência do que se está codificando, o que nunca é uma coisa ruim. Para mim, isso é como incluirvirtual
ao substituir: não precisamos, mas as coisas parecem muito mais intuitivas - e consistentes com outras declarações - quando fazemos.A declaração estática neste nível de código significa que a variável só é visível na unidade de compilação atual. Isso significa que apenas o código dentro desse módulo verá essa variável.
se você tiver um arquivo de cabeçalho que declara uma variável estática e esse cabeçalho está incluído em vários arquivos C / CPP, essa variável será "local" para esses módulos. Haverá N cópias dessa variável para os N lugares em que o cabeçalho está incluído. Eles não estão relacionados entre si de forma alguma. Qualquer código em qualquer um desses arquivos de origem fará referência apenas à variável declarada nesse módulo.
Nesse caso específico, a palavra-chave 'estática' não parece oferecer nenhum benefício. Posso estar faltando alguma coisa, mas parece que não importa - eu nunca vi nada assim antes.
Quanto ao inline, neste caso a variável provavelmente está inline, mas isso é apenas porque é declarada const. O compilador pode estar mais propenso a incorporar variáveis estáticas do módulo, mas isso depende da situação e do código que está sendo compilado. Não há garantia de que o compilador irá inline 'estática'.
fonte
const
, ostatic
é implícito e, portanto, opcional. O corolário é que não há suscetibilidade a erros de definição múltipla, como afirma Mike F.O livro C (online gratuito) tem um capítulo sobre ligação, que explica o significado de 'estático' em mais detalhes (embora a resposta correta já seja dada em outros comentários): http://publications.gbdirect.co.uk/c_book /chapter4/linkage.html
fonte
Para responder à pergunta, "o estático significa que apenas uma cópia do VAL é criada, caso o cabeçalho seja incluído por mais de um arquivo de origem?" ...
NO . VAL sempre será definido separadamente em cada arquivo que inclui o cabeçalho.
Os padrões para C e C ++ causam uma diferença neste caso.
Observe que os vinculadores modernos podem reclamar de ANOTHER_VAL se o cabeçalho for incluído em arquivos diferentes (mesmo nome global definido duas vezes), e definitivamente reclamariam se ANOTHER_VAL fosse inicializado com um valor diferente em outro arquivo
Você também precisa levar em consideração o fato de que ambas as variáveis são designadas como const. Idealmente, o compilador sempre escolheria incorporar essas variáveis e não incluir nenhum armazenamento para elas. Há uma série de razões pelas quais o armazenamento pode ser alocado. Eu posso pensar em ...
fonte
Supondo que essas declarações estejam no escopo global (ou seja, não sejam variáveis de membro), então:
estático significa 'ligação interna'. Neste caso, uma vez que é declarado const, pode ser otimizado / embutido pelo compilador. Se você omitir const , o compilador deve alocar armazenamento em cada unidade de compilação.
Ao omitir estático, a ligação é externa por padrão. Novamente, você foi salvo pela const ness - o compilador pode otimizar / usar em linha. Se você eliminar o const , obterá um erro de símbolos múltiplos definidos no momento do link.
fonte
Você não pode declarar uma variável estática sem defini-la também (isso ocorre porque os modificadores da classe de armazenamento static e extern 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 de origem que incluiu o arquivo de cabeçalho tivesse sua própria cópia privada da variável, o que provavelmente não é o que se pretendia.
fonte
Variáveis const são por padrão estáticas em C ++, mas externas C. Portanto, se você usar C ++, isso não fará sentido que construção usar.
(7.11.6 C ++ 2003 e Apexndix C tem amostras)
Exemplo em comparar fontes de compilação / link como programa C e C ++:
fonte
static
. Ele sinaliza a intenção / consciência do que o programador está fazendo e mantém a paridade com outros tipos de declaração (e, fwiw, C) que carecem do implícitostatic
. É como incluirvirtual
e ultimamenteoverride
em declarações de funções de override - não é necessário, mas muito mais autodocumentado e, no caso deste último, propício à análise estática.const
apenas em uma variável em um cabeçalho comg++ (GCC) 7.2.1 20170915 (Red Hat 7.2.1-2)
. Resultou em cerca de 150 símbolos múltiplos definidos (um para cada unidade de tradução em que o cabeçalho foi incluído). Acho que precisamos tantostatic
,inline
ou um anônimo / namespace sem nome para evitar a ligação externa.const int
dentro do escopo do namespace e no namespace global. E é compilado e segue a regra "Objetos declarados const e não explicitamente declarados extern têm ligação interna." ".... Talvez no projeto por algum motivo este cabeçalho incluído em fontes compiladas C, onde as regras são completamente diferentes.Static evita que outra unidade de compilação externe essa variável, de forma que o compilador possa apenas "embutir" o valor da variável onde ela é usada e não criar armazenamento de memória para ela.
Em seu segundo exemplo, o compilador não pode presumir que algum outro arquivo de origem não o externará, portanto, ele deve realmente armazenar esse valor em algum lugar da memória.
fonte
Estático impede que o compilador adicione várias instâncias. Isso se torna menos importante com a proteção #ifndef, mas assumindo que o cabeçalho esteja incluído em duas bibliotecas separadas e o aplicativo esteja vinculado, duas instâncias seriam incluídas.
fonte
static
"menos importante". e mesmo com ambos, você pode acabar com várias definições internamente vinculadas, o que provavelmente não era o pretendido.