2015-11-17 15 views
10
  • Senza #include<ctype.h>, i seguenti risultati del programma 1 e 0.
  • Con la includono, in output 1 e 1.

Sto usando TDM -GCC 4.9.2 64-bit. Mi chiedo quale sia l'implementazione di isdigit nel primo caso e perché è in grado di collegare.Perché io sono in grado di collegare senza includere ctype.h

#include<stdio.h> 
//#include<ctype.h> 
int main() 
{ 
    printf("%d %d\n",isdigit(48),isdigit(48.4)); 
    return 0; 
} 
+0

Potrebbe essere che ctype.h ridefinisce isdigit come macro? –

+0

@WouterHuysentruit no, non sto nascondendo nulla. Inoltre, se hai appena copiato il programma e incollato in ideone sembra compilare e linkare bene su GCC 5.1, ma si blocca. – user1537366

risposta

6

da GCC predefinito utilizza lo standard C90 (con estensioni GNU (reference)) che permette dichiarazioni implicite. Il problema con il tuo caso è che hai due chiamate allo isdigit con due diversi argomenti che potrebbero confondere il compilatore quando crea la dichiarazione implicita della funzione, e probabilmente seleziona int isdigit(double) per essere al sicuro. Questo è ovviamente il prototipo sbagliato per la funzione, il che significa che quando la funzione della libreria viene chiamata in fase di esecuzione verrà richiamata con argomenti errati e si avrà comportamento non definito.

Quando si include il file di intestazione <ctype.h>, v'è una corretta prototipo e poi il compilatore sa che isdigit prende un argomento int e può convertire il double letterale 48.4 al numero intero 48 per la chiamata.


Per quanto riguarda il motivo per cui è il collegamento, è perché mentre queste funzioni possono essere implementate come macro, che non è un requisito. Cosa è un requisito è che quelle funzioni, almeno nello standard C11 (non ho alcuna versione precedente disponibile al momento), devono essere consapevoli delle impostazioni locali correnti che renderanno la loro implementazione come macro molto più difficile, e molto più semplice delle normali funzioni di libreria. E poiché la libreria standard è sempre collegata (a meno che tu non dica diversamente a GCC) le funzioni saranno disponibili.

2

Prima di tutto le istruzioni #include non hanno nulla a che fare con linking. Ricordare qualsiasi cosa con un # in primo piano in C è pensato per il preprocessore, non per il compilatore o il linker.

Ma detto che la funzione deve essere collegata non è vero?

Facciamo i passaggi in passaggi separati.

$ gcc -c -Werror --std=c99 st.c 
st.c: In function ‘main’: 
st.c:5:22: error: implicit declaration of function ‘isdigit’ [-Werror=implicit-function-declaration] 
    printf("%d %d\n",isdigit(48),isdigit(48.4)); 
        ^
cc1: all warnings being treated as errors 

Bene, come si vede gint's lint (analizzatore statico) è in azione!

Qualunque cosa si procederà ad ignorarlo ...

$ gcc -c --std=c99 st.c 
st.c: In function ‘main’: 
st.c:5:22: warning: implicit declaration of function ‘isdigit’ [-Wimplicit-function-declaration] 
    printf("%d %d\n",isdigit(48),isdigit(48.4)); 

Questa volta solo un avvertimento. Ora abbiamo un file oggetto nella directory corrente. Andiamo a controllarla ...

$ nm st.o 
       U isdigit 
0000000000000000 T main 
       U printf 

Come si può vedere sia printf e isdigit è elencato come indefinito. Quindi il codice deve venire da qualche parte, vero?

procediamo a collegarlo ...

$ gcc st.o 
$ nm a.out | grep 'printf\|isdigit' 
       U [email protected]@GLIBC_2.2.5 
       U [email protected]@GLIBC_2.2.5 

Bene come potete vedere la situazione è leggermente migliorata. Come isdigit e printf non sono solitari indifesi come se fossero nello st.o. È possibile vedere che entrambe le funzioni sono fornite da GLIBC_2.2.5. Ma dov'è questo GLIBC?

Bene esaminiamo l'eseguibile finale un po 'più ...

$ ldd a.out 
     linux-vdso.so.1 => (0x00007ffe58d70000) 
     libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb66f299000) 
     /lib64/ld-linux-x86-64.so.2 (0x000055b26631d000) 

AHA ... non v'è che libc. Quindi, sebbene tu non abbia dato alcuna istruzione, il linker è collegato a 3 librerie per impostazione predefinita, una di queste è la libc che contiene sia printf e isdigit.

Si può vedere il comportamento predefinito del linker da:

$gcc -dumpspec 
*link: 
%{!r:--build-id} %{!static:--eh-frame-hdr} %{!mandroid|tno-android-ld:%{m16|m32|mx32:;:-m elf_x86_64}     %{m16|m32:-m elf_i386}     %{mx32:-m elf32_x86_64} --hash-style=gnu --as-needed %{shared:-shared} %{!shared:  %{!static:  %{rdynamic:-export-dynamic}  %{m16|m32:-dynamic-linker %{muclibc:/lib/ld-uClibc.so.0;:%{mbionic:/system/bin/linker;:/lib/ld-linux.so.2}}}  %{m16|m32|mx32:;:-dynamic-linker %{muclibc:/lib/ld64-uClibc.so.0;:%{mbionic:/system/bin/linker64;:/lib64/ld-linux-x86-64.so.2}}}  %{mx32:-dynamic-linker %{muclibc:/lib/ldx32-uClibc.so.0;:%{mbionic:/system/bin/linkerx32;:/libx32/ld-linux-x32.so.2}}}}  %{static:-static}};:%{m16|m32|mx32:;:-m elf_x86_64}     %{m16|m32:-m elf_i386}     %{mx32:-m elf32_x86_64} --hash-style=gnu --as-needed %{shared:-shared} %{!shared:  %{!static:  %{rdynamic:-export-dynamic}  %{m16|m32:-dynamic-linker %{muclibc:/lib/ld-uClibc.so.0;:%{mbionic:/system/bin/linker;:/lib/ld-linux.so.2}}}  %{m16|m32|mx32:;:-dynamic-linker %{muclibc:/lib/ld64-uClibc.so.0;:%{mbionic:/system/bin/linker64;:/lib64/ld-linux-x86-64.so.2}}}  %{mx32:-dynamic-linker %{muclibc:/lib/ldx32-uClibc.so.0;:%{mbionic:/system/bin/linkerx32;:/libx32/ld-linux-x32.so.2}}}}  %{static:-static}} %{shared: -Bsymbolic}} 

Quali sono le altre due biblioteche?

Bene ricordare quando si scavate nella a.out, sia printf e isdigit erano ancora indicato come U che significa sconosciuto. In altre parole, non c'erano gli indirizzi memory associati a questi simboli.

In realtà è qui che si trova la magia. Queste librerie sono state effettivamente caricate durante il runtime, non durante il tempo di collegamento come nei sistemi precedenti.

Come è implementato? Bene ha un gergo associato, qualcosa come collegamento pigro. Quello che fa, è quando il processo chiama una funzione, se non c'è alcun indirizzo di memoria (sezione TESTO), genera un Trap (Qualcosa come un'eccezione nel gergo della lingua di alto livello, quando il controllo viene passato al motore della lingua). Il kernel intercetta tale Trap e lo consegna al caricatore dinamico che carica la libreria e restituisce l'indirizzo di memoria associato al processo del chiamante.

Ci sono molti motivi teorici, perché fare cose pigramente è meglio che farlo in anticipo. Immagino sia un argomento completamente nuovo, di cui parleremo in un altro momento.

Problemi correlati