Definizioni sperimentali in C99 e collegamenti

Considera il programma C composto da due file,

f1.c:

int x; 

f2.c:

 int x=2; 

La mia lettura del paragrafo 6.9.2 della norma C99 è che questo programma dovrebbe essere respinto. Nella mia interpretazione di 6.9.2, la variabile x è definita provvisoriamente in f1.c , ma questa definizione provvisoria diventa una definizione effettiva alla fine dell’unità di traduzione, e (secondo me), dovrebbe quindi comportarsi come se f1.c contenesse la definizione int x=0; .

Con tutti i compilatori (e, soprattutto, i linker) che ho potuto provare, questo non è quello che succede. Tutte le piattaforms di compilazione che ho provato collegano i due file precedenti e il valore di x è 2 in entrambi i file.

Dubito che ciò accada per caso, o semplicemente come una caratteristica “facile” da fornire in aggiunta a ciò che richiede lo standard. Se ci pensate, significa che c’è un supporto speciale nel linker per quelle variabili globali che non hanno un inizializzatore, al contrario di quelle esplicitamente inizializzate a zero. Qualcuno mi ha detto che la funzione linker potrebbe essere necessaria per compilare Fortran comunque. Questa sarebbe una spiegazione ragionevole.

Qualche idea su questo? Altre interpretazioni dello standard? Nomi di piattaforms su cui i file f1.c e f2.c rifiutano di essere collegati insieme?

Nota: questo è importante perché la domanda si verifica nel contesto dell’analisi statica. Se i due file possono rifiutarsi di essere collegati su qualche piattaforma, l’analizzatore dovrebbe sporgere reclamo, ma se tutte le piattaforms di compilazione lo accettano, non c’è motivo di metterlo in guardia.

Vedi anche Cosa sono le variabili esterne in C. Questo è menzionato nello standard C nell’Allegato J informativo come estensione comune:

J.5.11 Definizioni esterne multiple

Potrebbero esserci più di una definizione esterna per l’identificatore di un object, con o senza l’uso esplicito della parola chiave extern; se le definizioni non sono d’accordo, o più di una è inizializzata, il comportamento non è definito (6.9.2).

avvertimento

Come sottolinea @litb qui, e come affermato nella mia risposta alla domanda incrociata, l’uso di più definizioni per una variabile globale porta a un comportamento indefinito, che è il modo standard per dire “qualsiasi cosa potrebbe accadere”. Una delle cose che possono accadere è che il programma si comporti come ci si aspetta; e J.5.11 dice, approssimativamente, “potresti essere fortunato più spesso di quanto meriti”. Ma un programma che si basa su più definizioni di una variabile esterna – con o senza la parola chiave ‘extern’ esplicita – non è un programma strettamente conforms e non è garantito che funzioni ovunque. Equivalentemente: contiene un bug che può o non può mostrare se stesso.

Esiste qualcosa chiamato “estensione comune” nello standard, in cui è ansible definire variabili più volte purché la variabile venga inizializzata una sola volta. Vedi http://c-faq.com/decl/decldef.html

La pagina collegata dice che questo è pertinente alle piattaforms Unix – immagino che sia lo stesso per C99 come C89 – anche se forse è stato adottato da più compilatori per formare una sorta di standard de facto. Interessante.

Questo per chiarire la mia risposta ad un commento di olovb:

output di nm per un file object compilato da “int x;”. Su questa piattaforma, i simboli sono preceduti da un ‘_’, cioè la variabile x appare come _x.

 00000000 T _main U _unknown 00000004 C _x U dyld_stub_binding_helper 

output di nm per un file object compilato da “int x = 1;”

 00000000 T _main U _unknown 000000a0 D _x U dyld_stub_binding_helper 

output di nm per un file object compilato da “int x = 0;”

 00000000 T _main U _unknown 000000a0 D _x U dyld_stub_binding_helper 

output di nm per un file object compilato da “extern int x;”

 00000000 T _main U _unknown U dyld_stub_binding_helper 

EDIT: output di nm per un file object compilato da “extern int x;” dove x è effettivamente utilizzato in una delle funzioni

 00000000 T _main U _unknown U _x U dyld_stub_binding_helper