declarações de variáveis ??em arquivos de cabeçalho - estática ou não?

StackOverflow https://stackoverflow.com/questions/92546

  •  01-07-2019
  •  | 
  •  

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?

Foi útil?

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 e ANOTHER_VAL é extern.

  • Em C ++, variáveis ??com escopo de arquivo são static (ligação interna) por padrão se forem const e extern por padrão, se eles não são. Se você estiver usando C ++, tanto VAL e ANOTHER_VAL são static.

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top