O @msc fornece uma boa introdução às regras por trás desse comportamento.
Percebi que se eu declarar uma variável global várias vezes, o compilador nem emitirá um aviso.
C tem três tipos de declarações globais para objetos, a saber, aquelas que são (e eu estou comentando static
aqui):
- declarações que não são definições -
extern int a;
- declarações que também são definições -
int a = 3;
ouextern int a = 3;
- definições provisórias -
int a;
Múltiplas declarações do tipo 1 e 3 são permitidas, enquanto no máximo uma definição (do tipo 2) é permitida.
Qual a explicação para esse comportamento?
Se você também está perguntando sobre a motivação dessas regras, é o suporte para compilação separada . (Ver unidade de tradução ).
Para dividir um programa em vários arquivos compilados separadamente, precisamos de alguns recursos, a saber: (a) poder declarar sem necessariamente definir e (b) declaração direta .
Dentro de uma unidade de tradução, precisamos nos referir a funções e dados globais em outra unidade de tradução. E também gostaríamos de uma verificação de erro, aqui, para descobrir definições ausentes e definições duplicadas erradas.
Às vezes, na mesma unidade de tradução, declaramos um global e o definimos mais tarde. Isso pode acontecer se precisarmos de uma declaração direta por algum motivo, ou se usarmos um arquivo de cabeçalho comum (que fornece declarações) dentro de uma unidade de tradução que também oferece definições explícitas.
Como a compilação separada em C se aplica ao vincular funções e dados globais, esses recursos são necessários no nível global, mas não no nível local.
Como o @msc aponta, nada disso é necessário para variáveis locais, pois elas não têm ligação.
C (como muitos outros idiomas) não fornece ligação para variáveis locais, pois o idioma não tenta suportar uma única função que abrange várias unidades de tradução separadas.
(Obviamente, você pode ter uma função em vários arquivos de origem, mas não em várias unidades de tradução.)
Uma definição provisória funciona como uma declaração, pois é permitida em várias unidades de tradução (e também combina bem com outras declarações). No entanto, se não houver uma definição (não tentativa) para o identificador em todo o programa, o conjunto de (uma ou mais) definições tentativas em várias unidades de conversão (para um identificador) será usado como uma definição para o objeto cujo inicializador é zero.
Isso pode ser implementado colocando-os na seção .BSS com o tamanho e o alinhamento adequados; o vinculador os corresponderá à definição verdadeira, se encontrado, ou corresponderá um ao outro, dando a eles espaço zerado no BSS.
A noção de compilação separada pode ser totalmente suportada sem o recurso de definições provisórias - acho que as definições provisórias existem principalmente por razões históricas. (Não estou dizendo que eles não são úteis, apenas se o idioma foi criado hoje, isso pode ser visto como desnecessário e, portanto, não ser oferecido.)