State violando "una regola di definizione" di C, e il risultato è un comportamento indefinito.La "regola della definizione unica" non è formalmente indicata nello standard in quanto tale. Stiamo esaminando oggetti in diversi file sorgente (ovvero unità di traduzione), quindi ci occupiamo di "definizioni esterne". Semantica "una definizione esterna" è precisato (C11 6.9 p5):
in modo da avere più definizioni di b
. Tuttavia, c'è un altro errore, in quanto è stato definito b
con tipi diversi. Prima nota che sono consentite più dichiarazioni allo stesso oggetto con collegamento esterno. Tuttavia, quando lo stesso nome viene utilizzato in due file sorgente diversi, tale nome fa riferimento allo stesso oggetto (C11 6.2.2 p2):
Nell'insieme di unità di traduzione e librerie che costituisce un intero programma, ciascuna La dichiarazione di un identificatore particolare con collegamento esterno indica lo stesso oggetto o la funzione .
C pone una limitazione rigorosa sul dichiarazioni allo stesso oggetto (C11 6.2.7 p2):
tutte le dichiarazioni che si riferiscono allo stesso oggetto o funzione devono avere tipo compatibile; in caso contrario, il comportamento non è definito.
Poiché i tipi per b
in ciascuno dei file di origine non corrispondono effettivamente, il comportamento non è definito. (Ciò che costituisce un tipo compatibile è descritto in dettaglio in tutta la C11 6.2.7, ma si riduce sostanzialmente verso il basso per essere che i tipi devono corrispondere.)
modo da avere due mancanze per b
:
- Definizioni multiple.
- Dichiarazioni multiple con tipi incompatibili.
Tecnicamente, la dichiarazione di int a
in entrambi i file di origine viola anche la "regola di una definizione". Si noti che a
ha linkage esterno (C11 6.2.2 p5):
Se la dichiarazione di un identificatore per un oggetto ha presentare portata e nessun deposito di classe specificatore, il suo collegamento è esterno.
Ma, dalla citazione da C11 6.9.2 in precedenza, quelle int a
definizioni timidi sono definizioni esterni, e vi sono ammessi solo uno di quelli dalla citazione da C11 6.9 in alto.
Le normali dichiarazioni di non responsabilità si applicano per comportamento non definito. Tutto può succedere, e questo includerebbe il comportamento che hai osservato.
Un'estensione comune a C è quello di consentire a più definizioni esterne, ed è descritto nello standard C nel informativo allegato J.5 (C11 J.5.11):
Ci possono essere più di una definizione esterna per l'identificatore di un oggetto, con o senza l'uso esplicito della parola chiave extern
; se le definizioni non sono in accordo con o più di una è inizializzata, il comportamento non è definito (6.9.2).
(corsivo è mio.) Dal momento che le definizioni di a
sono d'accordo, non c'è nulla di male lì, ma le definizioni per b
non sono d'accordo. Questa estensione spiega perché il compilatore non si lamenta della presenza di più definizioni. Dalla citazione di C11 6.2.2, il linker tenterà di riconciliare i riferimenti multipli allo stesso oggetto.
I linker utilizzano in genere uno dei due modelli per riconciliare più definizioni dello stesso simbolo in più unità di traduzione. Questi sono il "Modello comune" e il "Modello Rif/Def". Nel "Modello comune", più oggetti con lo stesso nome vengono piegati in un singolo oggetto in uno stile union
in modo che l'oggetto assuma le dimensioni della definizione più grande. Nel "Modello Ref/Def", ogni nome esterno deve avere esattamente una definizione.
La toolchain GNU utilizza il "Modello comune" per impostazione predefinita e un "Modello Ref/Def rilassato", in cui applica rigorosamente una regola di definizione per una singola unità di traduzione, ma non si lamenta delle violazioni su più unità di traduzione .
Il "Modello comune" può essere soppresso nel compilatore GNU utilizzando l'opzione -fno-common
.Quando ho provato questo sul mio sistema, che ha causato "rigorosa Rif/Def modello" comportamento per il codice simile al vostro:
$ cat a.c
#include <stdio.h>
int a;
struct { char a; int b; } b = { 2, 4 };
void foo() { printf("%zu\n", sizeof(b)); }
$ cat b.c
#include <stdio.h>
extern void foo();
int a, b;
int main() { printf("%zu\n", sizeof(b)); foo(); }
$ gcc -fno-common a.c b.c
/tmp/ccd4fSOL.o:(.bss+0x0): multiple definition of `a'
/tmp/ccMoQ72v.o:(.bss+0x0): first defined here
/tmp/ccd4fSOL.o:(.bss+0x4): multiple definition of `b'
/tmp/ccMoQ72v.o:(.data+0x0): first defined here
/usr/bin/ld: Warning: size of symbol `b' changed from 8 in /tmp/ccMoQ72v.o to 4 in /tmp/ccd4fSOL.o
collect2: ld returned 1 exit status
$
Personalmente ritengo l'ultimo monito lanciato dal linker dovrebbe sempre essere fornita indipendentemente dalla risoluzione modello per più definizioni di oggetti, ma non è né qui né là.
Riferimenti:
Unfortunately, I can't give you the link to my copy of the C11 Standard
What are extern
variables in C?
The "Beginner's Guide to Linkers"
SAS Documentation on External Variable Models
Qual è la tua domanda? –
Usa '% p' per stampare i puntatori, inoltre dovresti aggiungere' pippo' alla tua intestazione. – Nobilis
Se si desidera risolvere il conflitto, dichiarare la variabile var. – snf