Quiz – Definição múltipla de variáveis globais

definição

Quem ainda não sofreu com erro de compilação com relação à definição múltipla de uma mesma variável ou função? Pois bem, com o objetivo de ajudar a elucidar as dúvidas mais básicas de programação em C, preparamos um exemplo bem simples de um cenário de definição múltipla de variáveis globais num projeto. Veja um exemplo:

main.c

 bar.c

 bar.h

 Compilação

O que acontece na compilação? Ela é bem sucedida? Ou, ainda, ocorre um erro na link-edição do binário executável? Pode-se, também, gerar um arquivo executável com sucesso, no entanto a sua execução poderia apresentar um comportamento indesejado. Participem desse quiz e fiquem à vontade para comentar!

Resposta

Chegou a hora da resposta! Esse quiz contou com 52 votos. As alternativas oferecidas e os respectivos números de votos são:

As regras do jogo são estabelecidas pelo padrão da linguagem C. É um trabalho de advogado! Consultando o documento ISO/IEC 9899:TC3, conhecido como C99, no item 6.9.2, temos o seguinte texto:

6.9.2 External object definitions

Semantics

1 If the declaration of an identifier for an object has file scope and an initializer, the declaration is an external definition for the identifier.

2 A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.

No nosso exemplo, a declaração de foo em bar.c é uma definição, pois requisita ao compilador a alocação de espaço na memória do sistema para a variável em questão. Trata-se de um identicador com linkagem externa e escopo de arquivo. A seguir podemos ver como o compilador tratou essa variável:

O compilador alocou a variável foo na seção de dados inicializados. Perfeito! Essa inicialização é realizada antes da chamada à função main(), na etapa de start-up.

Mas o que acontece com a declaração da variável foo em main.c? Ela é tratada, de acordo com o padrão da linguagem, como uma tentative definition com linkagem externa, pois não oferece um inicializador e não contém o especificador de armazenamento static. Esse tipo de identificador é tratado no GCC como um símbolo comum, como pode ser observado no arquivo binário, no formato ELF, main.o:

Esse tipo de símbolo representa dados não inicializados. Múltiplos símbolos comuns podem existir ao longo de outros arquivos-objeto, sendo que somente uma definição com um inicializador pode existir. Quando essa última é encontrada pelo linker, as outras declarações são tratadas como referências não definidas.

Veja que no binário executável ELF final, o símbolo foo é alocado na seção de dados inicializados.

Portanto, não existe erro nem de compilação, nem de link-edição. E a resposta é:

Acharam que era outra resposta? Deixem seus comentários justificando as suas escolhas!

Muito obrigado pessoal pela participação! Teremos outros quizzes pela frente. Quer saber quando teremos mais um? Estejam ligados nas nossas redes sociais:

Facebook: https://www.facebook.com/osembarcados

LinkedIn: http://www.linkedin.com/groups/Os-Embarcados-6511589

Twitter: @embarcados