2013-03-24 9 views
6

CCome funziona il referenziamento quando si hanno due file .c con variabili globali di simboli uguali ma di tipi diversi?

Dire che ho i seguenti moduli C:

MODULO 1

#include <stdio.h> 
int x; 
int main(){ 
    foo(); 
    printf("%i\n",x); 
    return 0; 
} 

MODULO 2

double x; 
void foo(){ 
    x = 3.14; 
} 

La mia domanda è: che cosa fa il linker fare in questo Astuccio? Nel libro di testo che sto leggendo dice che il compilatore sceglie solo una delle due variabili globali deboli per la tabella dei simboli del linker. Quale di questi due è scelto? O sono entrambi scelti? Se è così, perché? Grazie.

risposta

5

C dice che è un comportamento non definito.

(C99, 6.9p5) "Se un identificatore dichiarato con collegamento esterno è utilizzato in un'espressione (non come parte della operando di un operatore sizeof cui risultato è un numero intero costante), da qualche parte in tutto il programma vi saranno esattamente una definizione esterna per l'identificatore, altrimenti non ci sarà più di un"

essere comportamento indefinito significa un linker può interrompere il processo di collegamento in presenza di più definizioni di oggetti esterni.

Ora linker sono belle (o male, è possibile scegliere) e di solito hanno estensioni predefinite per gestire molteplici definizioni di oggetto esterno e non fallire in alcuni casi.

Se si utilizza gcc e ld da binutils, si otterrà un errore se i due oggetti sono inizializzati in modo esplicito. Ad esempio, hai int x = 0; nella prima unità di traduzione e double x = 0.0;.

Altrimenti, se uno degli oggetti esterni non è inizializzato esplicitamente (la situazione nell'esempio) gcc combina silenziosamente i due oggetti in un unico simbolo. Puoi comunque chiedere al linker di segnalare un avviso passandogli l'opzione --warn-common.

Per esempio, quando il collegamento dei moduli:

gcc -Wl,--warn-common module1.o module2.o

per ottenere il processo di collegamento interrotta, è possibile richiedere il linker per il trattamento di tutti gli avvisi come errori utilizzando --fatal-warnings opzione (-Wl,--fatal-warnings,--warn-common).

Un altro modo per interrompere il processo di collegamento è utilizzare l'opzione del compilatore -fno-common, come spiegato da @teppic nella sua risposta. -fno-common proibisce agli oggetti esterni di ottenere un tipo di simbolo comune durante la compilazione. Se lo fai per entrambi i moduli e poi link, riceverai anche l'errore di linker a definizione multipla.

gcc -Wall -fno-common -c module1.c module2.c

gcc module1.o module2.o

+0

Quindi, in questo caso c'è solo una "x" nella tabella dei simboli? Posso vedere alcuni bug strani derivanti da quello. Thx – amorimluc

+0

@amorimluc nel tuo caso è probabile che ti piaccia ottenere un errore dal linker o da un singolo oggetto nel binario finale e il tuo programma invocherà un comportamento indefinito. – ouah

+0

Pensavo di sì, ma si compila e si collega bene. E x è stampato come int ma con il modello di bit del float 3.14. Questo probabilmente non succede molto nella vita reale, ma sarebbe sicuramente difficile trovare questo sottile bug in un grande programma ... – amorimluc

1

Se l'applicazione supporta più definizioni esterne, si ritroverà con un oggetto che è effettivamente lanciato ad ogni tipo in ogni modulo, come in una sorta di variabile unione implicita. La quantità di memoria per il tipo più grande verrà allocata e entrambi si comportano come dichiarazioni esterne.

Se si compila utilizzando clang o gcc, utilizzare l'opzione -fno-common per causare un errore.

Ecco la sezione del manuale di gcc:

 In C code, controls the placement of uninitialized global 
     variables. Unix C compilers have traditionally permitted multiple 
     definitions of such variables in different compilation units by 
     placing the variables in a common block. This is the behavior 
     specified by -fcommon, and is the default for GCC on most targets. 
     On the other hand, this behavior is not required by ISO C, and on 
     some targets may carry a speed or code size penalty on variable 
     references. The -fno-common option specifies that the compiler 
     should place uninitialized global variables in the data section of 
     the object file, rather than generating them as common blocks. 
     This has the effect that if the same variable is declared (without 
     "extern") in two different compilations, you will get a multiple- 
     definition error when you link them. 

Questa opzione fa rispettare in modo efficace rispetto rigoroso ISO C rispetto alla più definizioni.

Questo comportamento è generalmente accettato per le variabili esterne dello stesso tipo. Come afferma il manuale GCC, la maggior parte dei compilatori supportano questo, e (fornendo i tipi sono gli stessi), lo standard C99 definisce il suo uso come un'estensione.

Problemi correlati