Definições provisórias em C99 e vinculação

Considere o programa C composto de dois arquivos,

f1.c:

int x; 

f2.c:

 int x=2; 

Minha leitura do parágrafo 6.9.2 da norma C99 é que esse programa deve ser rejeitado. Na minha interpretação de 6.9.2, a variável x é provisoriamente definida em f1.c , mas essa definição provisória se torna uma definição real no final da unidade de tradução, e (na minha opinião), deve se comportar como se f1.c contivesse a definição int x=0; .

Com todos os compiladores (e, mais importante, linkers) eu pude tentar, isso não é o que acontece. Todas as plataformas de compilation que tentei vincularam os dois arquivos acima e o valor de x é 2 em ambos os arquivos.

Eu duvido que isso aconteça por acidente, ou apenas como um recurso “fácil” para fornecer além do que a norma exige. Se você pensar sobre isso, significa que há suporte especial no vinculador para as variables ​​globais que não possuem um inicializador, em oposição àquelas explicitamente inicializadas como zero. Alguém me disse que o recurso de linker pode ser necessário para compilar o Fortran de qualquer maneira. Essa seria uma explicação razoável.

Alguma idéia sobre isso? Outras interpretações do padrão? Nomes de plataformas nas quais os arquivos f1.c e f2.c recusam a serem vinculados?

Nota: isso é importante porque a questão ocorre no contexto da análise estática. Se os dois arquivos podem se recusar a ser vinculados em alguma plataforma, o analisador deve reclamar, mas se toda plataforma de compilation aceita, então não há razão para avisá-lo.

Veja também O que são variables ​​externas em C. Isto é mencionado no padrão C no Anexo J informativo como uma extensão comum:

J.5.11 Múltiplas definições externas

Pode haver mais de uma definição externa para o identificador de um object, com ou sem o uso explícito da palavra-chave extern; se as definições discordarem ou mais de uma for inicializada, o comportamento é indefinido (6.9.2).

Atenção

Como o @litb aponta aqui, e como declarado na minha resposta à questão de referência cruzada, o uso de múltiplas definições para uma variável global leva a um comportamento indefinido, que é a maneira padrão de dizer “tudo pode acontecer”. Uma das coisas que podem acontecer é que o programa se comporta como você espera; e J.5.11 diz, aproximadamente, “você pode ter mais sorte do que merece”. Mas um programa que se baseia em múltiplas definições de uma variável externa – com ou sem a palavra-chave ‘extern’ explícita – não é um programa estritamente conforme e não é garantido que funcione em qualquer lugar. Equivalente: contém um bug que pode ou não aparecer.

Existe algo chamado de “extensão comum” para o padrão, onde a definição de variables ​​múltiplas vezes é permitida desde que a variável seja inicializada apenas uma vez. Veja http://c-faq.com/decl/decldef.html

A página vinculada diz que isso é pertinente para plataformas Unix – eu acho que é o mesmo para c99 como c89 – embora talvez tenha sido adotado por mais compiladores para formar algum tipo de padrão de definição. Interessante.

Isso é para esclarecer minha resposta a um comentário de olovb:

Saída de nm para um arquivo de object compilado de “int x;”. Nesta plataforma, os símbolos são prefixados com um ‘_’, isto é, a variável x aparece como _x.

 00000000 T _main U _unknown 00000004 C _x U dyld_stub_binding_helper 

saída de nm para um arquivo de object compilado de “int x = 1;”

 00000000 T _main U _unknown 000000a0 D _x U dyld_stub_binding_helper 

saída de nm para um arquivo de object compilado de “int x = 0;”

 00000000 T _main U _unknown 000000a0 D _x U dyld_stub_binding_helper 

saída de nm para um arquivo de object compilado de “extern int x;”

 00000000 T _main U _unknown U dyld_stub_binding_helper 

EDIT: saída de nm para um arquivo de object compilado de “extern int x;” onde x é realmente usado em uma das funções

 00000000 T _main U _unknown U _x U dyld_stub_binding_helper