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:

  • Mensagens: foo = 2 foo de bar = 2 (12 votos - 23%)
  • Mensagens: foo = 0 foo de bar = 0 (3 votos - 6%)
  • Erro de compilação (10 votos - 19%)
  • Erro de link-edição (27 votos - 52%)

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

 

NEWSLETTER

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Obrigado! Sua inscrição foi um sucesso.

Ops, algo deu errado. Por favor tente novamente.

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Henrique Rossi
Engenheiro eletricista com ênfase em eletrônica e pós-graduado em Engenharia de Software. Comecei um mestrado, mas o interrompi. Especialista na área de sistemas embarcados, com mais de 12 anos de experiência em desenvolvimento de firmware (sistemas baremetal e baseados em RTOS) e Linux Embarcado. Atualmente sou administrador do site Embarcados, trabalho num fabricante de Set-Top Box e atuo como consultor/desenvolvedor na área de sistemas embarcados.

4
Deixe um comentário

avatar
 
2 Comment threads
2 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
2 Comment authors
Henrique RossiAdriano Oliveira Pires Recent comment authors
  Notificações  
recentes antigos mais votados
Notificar
Adriano Oliveira Pires
Visitante
AdrianoOP

Se eu tivesse:

no arquivo bar.c:

int foo = 2;

void bar_print_foo(void)
{
foo = 0;
printf("foo de bar = %dn", foo);
}

e no main.c:

int foo;

int main(void)
{
foo = 1;
printf("foo = %dn", foo);
bar_print_foo();

return 0;
}

qual seria o resultado?

Henrique Rossi
Visitante

Adriano,

O resultado seria:

foo = 1
foo de bar = 0

No exemplo mencionado não são alteradas as declarações/definições da variável foo, e sim somente são adicionadas algumas atribuições à ela ao longo do código. Portanto:

A variável foo, antes de ser alterada pela função main(), é inicializada com o valor 2. Em main() seu valor é modificado explicitamente para 1, o que faz a chamada à função printf() exibir a saída:

foo = 1

Quando a função bar_print_foo() é chamada, o valor da variável foo é alterado novamente, agora para 0. Assim, a chamada à função printf exibi a saída:

foo de bar = 0

Abraços!

Adriano Oliveira Pires
Visitante
AdrianoOP

Hahaha! Muito bom!

Será que existe algum tipo de diretiva de compilação que possa alterar este resultado ou causar o erro "esperado" (erro de link de acordo com 52% das respostas)?

Henrique Rossi
Visitante

Olá Adriano!

Muito boa a pergunta! Não existe uma diretiva de compilação para gerar esse comportamento. Mas existe uma forma de causar um erro de link-edição: criando duas definições da variável foo, ambas com linkagem externa e com inicializadores. Exemplo:

Em main.c, altere a declaração da variável foo, por exemplo, para:

int foo = 3;

O valor de inicialização não importa, contanto que ele seja explícito.

Compile! O erro de link-edição ocorre, mencionando que uma declaração múltipla da variável foo foi encontrada.

Abraços,
Henrique