2010-02-25 32 views
17

Quando scrivo il seguente programma:implementazione variabile globale

file di 1:

#include <stdio.h>  
int global;  
void print_global1() { 
     printf("%p\n", &global); 
} 

file di 2:

#include <stdio.h> 
char global;  
void print_global2() { 
     printf("%p\n", &global); 
} 

file di 3:

void print_global1(); 
void print_global2(); 
int main() 
{ 
     print_global1(); 
     print_global2(); 

     return 0; 
} 

uscita:

$ ./a.out 
0x804a01c 
0x804a01c 

Ecco la mia domanda:

  • Perché i linker attuando "int globale" e "char globale" come lo stesso variabile globale:
  • Come mai il compilatore non si lamenta (non il più piccolo avviso con -Wall -Wextra -ansi ...)
  • Come sono le dimensioni della variabile globale gestito (la dimensione di int e char sono diversi)

PS: La seconda domanda è relativa all'architettura/compilatore, quindi prendiamo il gcc o Visual C++ (per C) con la dimensione int come 32 bit

MODIFICA: QUESTA NON E 'UNA DOMANDA PER C++ MA C!

Io uso gcc version 4.4.1 e su Ubuntu 9.10, ecco l'output della console di compilazione:

$ ls 
global_data1.c global_data2.c global_data.c 

$ gcc -Wall -Wextra -ansi global_data*.c 
$ ./a.out 
0x804a01c 
0x804a01c 
or 
$ gcc -Wall -Wextra -ansi -c global_data*.c 
$ gcc -Wall -Wextra -ansi global_data*.o 
$ ./a.out 
0x804a01c 
0x804a01c 

risposta

12

gcc non segnalare errori/avvertenze. Ma g++ fa.

EDIT:

Sembra C permette tentative definitions per una variabile.

Nel tuo caso sia le definizioni globali sono indicative e in tal caso è scelto il primo visto dal linker.

Cambia la tua file2 a:

char global = 1; // no more tentative...but explicit. 

Ora, se si compila come prima, il DEF evidente problema tattico, file1 verrà ignorato.

fare sia il DEF esplicita:

int global = 1; // in file1 

char global = 1; // in file2 

ora non può essere ignorato e otteniamo l'errore DEF multipla.

+0

Non sto facendo C++ ma programma C (questo è il motivo per cui non ho inserito il flag C++ nella mia risposta)! Sembra che la specifica C standard permetta questo comportamento (ma non ne sono proprio sicuro). Posso ancora usare il linker C++ per verificare se esiste una definizione multipla ma non sono sicuro se sia davvero sicuro ... – Phong

+0

@Phong: hai ragione ... il C std lo consente. Ho aggiornato la mia risposta. – codaddict

+0

@codaddict: +1, Grazie per l'aggiornamento rapido! Ora capisco meglio come è gestito dal compilatore. – Phong

1

quale compilatore stai usando. Qual è la piattaforma? Con g ++ ottengo

/tmp/cc8Gnf4h.o:(.bss+0x0): multiple definition of `global' 
/tmp/ccDQHZn2.o:(.bss+0x0): first defined here 
/usr/bin/ld: Warning: size of symbol `global' changed from 4 in a.o to 1 in b.o 

AFAIR, in C++ le variabili nelle diverse unità di traduzione molto hanno la stessa dichiarazione esattamente al lavoro.

1

Il linker consente di avere dati esterni duplicati come questo (anche se sono sorpreso che i diversi tipi non causino problemi). Quale si ottiene dipende l'ordine dei file oggetto sulla riga di comando LINK.

+0

+1 C'è uno strumento che mi consenta di conoscere la dimensione della variabile "globale" ?. – Phong

5

Questo ha a che fare con qualcosa chiamato "definizione provvisoria" in C. Innanzitutto, se si assegna a global in entrambi file1 e file2, si otterrà un errore in C. Questo perché global non è provvisoriamente definito in file1 e file2, è veramente definito.

Dalla serie C (sottolineatura mia):

Una dichiarazione di un identificatore per un oggetto che ha presentare portata senza un inizializzatore, e senza un identificatore classe di archiviazione o con l'archiviazione di classe specificatore statico, costituisce una definizione provvisoria . Se un'unità di traduzione contiene una o più definizioni provvisorie per un identificatore e l'unità di traduzione non contiene alcuna definizione esterna per quell'identificatore, allora il comportamento è esattamente come se l'unità di traduzione contenga una dichiarazione dell'ambito del file di quell'identificatore, con il tipo composto come della fine dell'unità di traduzione, con un inizializzatore uguale a 0.

Per il tuo caso, "unità di traduzione" (in pratica) ogni file sorgente.

chi "tipi compositi":

Per un identificatore con collegamento interno o esterno dichiarata in un ambito in cui una prima dichiarazione di tale identificatore è visibile, se la dichiarazione preventiva specifica linkage interno o esterno , il tipo dell'identificatore nella dichiarazione successiva diventa il tipo composto .

Per ulteriori informazioni sulle definizioni provvisorie, vedere this question and its answers.

Sembra che per il tuo caso, dovrebbe essere un comportamento indefinito perché global è definito alla fine delle unità di traduzione, quindi ottieni due definizioni di global e, quel che è peggio, sono diverse. Sembra che il linker di default non si lamenta di questo però.

GNU ld ha un'opzione chiamata --warn-common, che si avverte per più definizioni di tentativi (simbolo comune è il nome del linker per le variabili provvisoriamente definite):

$ gcc -Wl,--warn-common file*.c 
/tmp/ccjuPGcq.o: warning: common of `global' overridden by larger common 
/tmp/ccw6nFHi.o: warning: larger common is here 

Dal manual:

Se c'è sono solo (uno o più) simboli comuni per una variabile, vanno nell'area dati non inizializzata del file di output. Il linker unisce più simboli comuni per la stessa variabile in un singolo simbolo. Se sono di dimensioni diverse, raccolgono le dimensioni maggiori. Il linker trasforma un simbolo comune in una dichiarazione, se esiste una definizione della stessa variabile.

L'opzione --warn-common può produrre cinque tipi di avvisi. Ogni avviso consiste in una coppia di linee: la prima descrive il simbolo appena incontrato e la seconda descrive il simbolo precedente incontrato con lo stesso nome.Uno o entrambi i simboli saranno un simbolo comune.

+0

@alok: +1 Grazie per avermi insegnato l'opzione -Wl, - warn-common gcc. – Phong