2010-09-11 18 views

risposta

27

gcc, in modalità C:

non inizializzate globali che non sono dichiarati extern sono trattati come simboli "comuni", non simboli deboli.

I simboli comuni vengono uniti al momento del collegamento in modo che si riferiscano tutti alla stessa memoria; se più di un oggetto tenta di inizializzare un tale simbolo, si verificherà un errore di collegamento. (Se non sono esplicitamente inizializzate ovunque, saranno messi in BSS, cioè inizializzato a 0.)

gcc, in C++ modalità:

Non lo stesso - non fa il cosa simboli comuni. I totali "non inizializzati" che non sono dichiarati extern vengono inizializzati implicitamente su un valore predefinito (0 per i tipi semplici o costruttore predefinito).


In entrambi i casi, un simbolo debole permette un simbolo inizializzato per essere sovrascritta da un simbolo non debole inizializzato con lo stesso nome in fase di collegamento.


Per illustrare (concentrandosi sul caso C qui), userò 4 varianti di un programma principale, che sono tutti uguali tranne che per il modo in cui viene dichiarata global:

  1. main_init.c:

    #include <stdio.h> 
    
    int global = 999; 
    
    int main(void) { printf("%d\n", global); return 0; } 
    
  2. main_uninit.c, whic h omette l'inizializzazione:

    #include <stdio.h> 
    
    int global; 
    
    int main(void) { printf("%d\n", global); return 0; } 
    
  3. main_uninit_extern.c, che aggiunge la parola chiave extern:

    #include <stdio.h> 
    
    extern int global; 
    
    int main(void) { printf("%d\n", global); return 0; } 
    
  4. main_weak_init.c, che inizializza global e dichiara di essere un simbolo debole:

    #include <stdio.h> 
    
    int global __attribute__((weak)) = 999; 
    
    int main(void) { printf("%d\n", global); return 0; } 
    

e another_def.c che inizializza lo stesso globale:

int global = 1234; 

Utilizzando main_uninit.c da sola dà 0:

$ gcc -o test main_uninit.c && ./test 
0 

ma quando another_def.c è incluso pure, global è esplicitamente inizializzato ed otteniamo il risultato atteso:

$ gcc -o test main_uninit.c another_def.c && ./test 
1234 

(Nota che questo caso fallisce invece se stai usando C++.)

Se proviamo sia con main_init.c e another.def.c invece, abbiamo 2 Inizializzazioni di global, che non funzioneranno:

$ gcc -o test main_init.c another_def.c && ./test 
/tmp/cc5DQeaz.o:(.data+0x0): multiple definition of `global' 
/tmp/ccgyz6rL.o:(.data+0x0): first defined here 
collect2: ld returned 1 exit status 

main_uninit_extern.c da sola non funziona affatto - la parola chiave extern fa sì che il simbolo di essere un riferimento esterno ordinario piuttosto che un simbolo comune, in modo che il linker si lamenta:

$ gcc -o test main_uninit_extern.c && ./test 
/tmp/ccqdYUIr.o: In function `main': 
main_uninit_extern.c:(.text+0x12): undefined reference to `global' 
collect2: ld returned 1 exit status 

funziona bene una volta che l'inizializzazione da another_def.c i s incluso:

$ gcc -o test main_uninit_extern.c another_def.c && ./test 
1234 

Utilizzando main_init_weak.c da solo dà il valore che inizializzato il simbolo debole (999), in quanto non v'è nulla di ignorarlo:

$ gcc -o test main_init_weak.c && ./test 
999 

Ma tirando in in questo caso l'altra definizione da another_def.c funziona in questo caso, poiché la definizione forte sostituisce la definizione debole in main_init_weak.c:

$ gcc -o test main_init_weak.c another_def.c && ./test 
1234 
+0

Bella risposta, grazie. Posso chiedere se si applica a C++? –

+0

Ottima spiegazione con esempi. Potresti includere anche quanto debole interagisce con le funzioni? – Tomek

+0

@Maciej - buon punto, ho dimenticato che questo è stato taggato con entrambi quando ho scritto la mia risposta in origine! No, non è lo stesso per C++, quindi ho aggiornato la mia risposta di conseguenza. –

3

È questo che intendevi?

weak.c

#include <stdio.h> 

int weak; /* global, weak, zero */ 

int main(void) { 
    printf("weak value is %d.\n", weak); 
    return 0; 
} 

strong.c

int weak = 42; /* global, strong, 42 */ 

conduzione Esempio

$ gcc weak.c 
$ ./a.out 
weak value is 0. 
$ gcc weak.c strong.c 
$ ./a.out 
weak value is 42.

Il int weak; in weak.c è una dichiarazione, non una definizione. Oppure potresti dire che è una definizione provvisoria. La definizione reale è strong.c quando il file oggetto è collegato nel programma finale o in weak.c in caso contrario. Questa è un'estensione comune, una che gcc utilizza (grazie Andrey).

+2

Non vero. 'int weak' in' weak.c' è una definizione.Un tentativo, ma definizione comunque. Il programma sopra * non * viola le regole della lingua, tuttavia è supportato come un'estensione non standard. – AnT

3

Qualsiasi definizione multipla di un simbolo globale è un comportamento non definito, quindi gcc (o meglio il linker GNU binutils) è libero di fare ciò che vuole. In pratica, segue il comportamento tradizionale per evitare di rompere il codice che si basa su questo comportamento.

+0

Vuoi dire più ** definizione **? – pmg

+0

Sì, aggiustandolo. –

9

La domanda è basata su una premessa errata. Le variabili globali non inizializzate non sono simboli deboli.

Apparentemente la domanda si riferisce alla capacità di definire lo stesso oggetto non inizializzato con il collegamento esterno in più unità di traduzione. Formalmente, non è permesso - è un errore sia in C che in C++.Tuttavia, almeno in C è riconosciuto dalla norma C99 come "Estensione comune" del linguaggio, implementata in molti compilatori della vita reale

J.5 estensioni comuni

J.5.11 multipla esterna definizioni

ci possono essere più di uno esterno definizione per l'identificatore di un oggetto , con o senza l'esplicito utilizzo della parola chiave extern; se le definizioni non sono d'accordo, o più di una è inizializzata, il comportamento è indefinito (6.9.2).

Nota: contrariamente alla credenza popolare, il linguaggio C proibisce esplicitamente l'introduzione di più definizioni di entità con collegamento esterno nel programma, proprio come fa il C++.

6.9 Definizioni esterne

Una definizione esterna è una dichiarazione esterna che è anche una definizione di una funzione (diverso una definizione di linea) o un oggetto. Se un identificatore dichiarato con esterna collegamento viene utilizzato in un'espressione (a titolo non dell'operando di un operatore sizeof cui risultato è una costante intero), in qualche parte l'intero programma vi sarà esattamente uno definizione esterna per l'identificatore ; in caso contrario, deve essere non più di uno.

Tuttavia, l'estensione che consente questo è stato molto popolare con molti compilatori C, di cui GCC è semplicemente uno.

+1

Per implementare i blocchi comuni di FORTRAN è richiesta la funzione di linker di supporto. Si noterà che GCC include un compilatore FORTRAN nella raccolta. Poiché le librerie di supporto sono implementate in C, non sorprende che ci sia una piccola quantità di perdite innocue dal linker. – RBerteig

+0

Puoi dare un esempio di definizione esterna e dichiarazione esterna? – Thomson

+0

Non sono solo FORTRAN, anche le funzioni inline C++ e i modelli implicitamente istanziati sono deboli. – MSalters

Problemi correlati