2013-02-22 11 views
9

sono sicuro che sto impazzendo, ma si consideri il seguente codice C:L'extern è facoltativo?

// file1.c 
int first; 

void f(void) 
{ first = 2; } 

// file2.c 
#include <stdio.h> 

int first; 
void f(); 
int main(void) 
{ 
    first = 1; 
    f(); 
    printf("%d", first); 
} 

Questi due file, per qualche ragione si compilare e collegare insieme, e la stampa 2. Ho sempre avuto l'impressione che se non avessi etichettato l'una o l'altra (ma non entrambe) le definizioni di first con extern, questo non sarebbe stato compilato, e quello era in realtà il punto intero di extern!

+0

quando si dichiara la funzione come extern, quindi il linker lo cercherà in un altro oggetto. Altrimenti lo cercherà nello stesso file. – kofemann

+0

Questo non dovrebbe nemmeno collegare. Sei sicuro di mostrare tutti gli avvisi del compilatore e del linker? –

+0

@KerrekSB Collegamenti perfettamente soddisfacenti con GCC. La stampa di '& first' da' f' e 'main' produce lo stesso indirizzo. –

risposta

5

In condizioni normali (nessun flag gcc in più) si dovrebbe andare bene per compilare il codice come:

gcc file1.c file2.c 

Che cosa sta per accadere è il compilatore vedrà che ci sono due variabili globali chiamato la stessa cosa e nessuno dei due è inizializzato. Quindi posizionerà le variabili globali non inizializzate nella sezione "comune" del codice **. In altre parole, avrà solo una copia della "prima" variabile. Questo accade perché il valore predefinito per gcc è -fcommon

Se si dovesse compilare con la bandiera -fno-common si sarebbe ora visualizzato l'errore stavi pensando di:

/tmp/ccZNeN8c.o:(.bss+0x0): multiple definition of `first' 
/tmp/cc09s2r7.o:(.bss+0x0): first defined here 
collect2: ld returned 1 exit status 

Per risolvere questo ci si aggiunge a extern tutti tranne una delle variabili.

ATTENZIONE:
Ora diciamo che hai avuto due array non inizializzate globali di diverse dimensioni:

// file1.c 
int first[10]; 

// file2.c 
int first[20]; 

Beh indovinate un po ', li compila con gcc -Wall file1.c file2.c produce senza avvisi o errori e la variabile è stato reso comune anche se è di dimensioni diverse !!!

//objdump from file1.c: 
0000000000000028  O *COM* 0000000000000020 first 

//objdump from file2.c: 
0000000000000050  O *COM* 0000000000000020 first 

Questo è uno dei pericoli delle variabili globali.


** Se si guarda a un objdump dei file * .o (è necessario compilare con gcc -c per generarli) vedrete first collocato nella sezione comune (*COM*):

[email protected]:~/C$ objdump -t file2.o 

a.o:  file format elf64-x86-64 

SYMBOL TABLE: 
0000000000000000 l df *ABS* 0000000000000000 file2.c 
0000000000000000 l d .text 0000000000000000 .text 
0000000000000000 l d .data 0000000000000000 .data 
0000000000000000 l d .bss 0000000000000000 .bss 
0000000000000000 l d .rodata 0000000000000000 .rodata 
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack 
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame 
0000000000000000 l d .comment 0000000000000000 .comment 
0000000000000004  O *COM* 0000000000000004 first 
0000000000000000 g  F .text 0000000000000039 main 
0000000000000000   *UND* 0000000000000000 f 
0000000000000000   *UND* 0000000000000000 printf 
9

Compilare solo perché first è solo dichiarato due volte, in realtà non ci sono due posti in memoria ma solo uno. Per prima cosa inizializza quello con int first=4; e l'altro con int first=5; e il tuo linker mostrerà l'errore, ad es. GCC:

b.o:b.c:(.data+0x0): multiple definition of `_first' 
a.o:a.c:(.data+0x0): first defined here 
collect2.exe: error: ld returned 1 exit status 
+0

Buona risposta, ++ – SomeWittyUsername

+0

No, questo è sbagliato. Lo standard C dice che non dovrebbe essere compilato, gcc (insieme al linker) ha un'ottimizzazione che lo fa funzionare fino a quando non si assegna un valore. –

+0

@ChrisJefferson stai dicendo quello che stavo dicendo –

Problemi correlati