2015-11-17 5 views
10
  • Ohne #include<ctype.h> die folgenden Programm-Ausgänge 1 und 0
  • Mit dem umfassen, es gibt 1 und 1.

ich TDM bin mit -GCC 4.9.2 64-Bit. Ich frage mich, was die Implementierung von isdigit im ersten Fall ist, und , warum es verknüpfen kann.Warum bin ich ohne einschließlich verknüpfen können ctype.h

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

Könnte es sein, dass ctype.h isdigit als Makro neu definiert? –

+0

@WouterHuysentruit nein, ich verstecke nichts. Außerdem, wenn Sie das Programm einfach kopiert und in ideone eingefügt haben, scheint es auf GCC 5.1 gut zu kompilieren und zu verlinken, aber es stürzt ab. – user1537366

Antwort

6

Standardmäßig verwendet die GCC C90-Standard (mit GNU Verlängerungen (reference)), die implizite Erklärungen ermöglicht. Das Problem mit Ihrem Fall besteht darin, dass Sie zwei Aufrufe an isdigit mit zwei verschiedenen Argumenten haben, die den Compiler beim Erstellen der impliziten Deklaration der Funktion verwirren könnten und wahrscheinlich int isdigit(double) auswählt, um auf der sicheren Seite zu sein. Das ist natürlich der falsche Prototyp für die Funktion, was bedeutet, dass wenn die Bibliotheksfunktion zur Laufzeit aufgerufen wird, sie mit falschen Argumenten aufgerufen wird und Sie haben undefiniertes Verhalten.

Wenn Sie die <ctype.h> Header-Datei gibt es einen richtigen Prototyp, und dann der Compiler weiß, dass isdigit ein int Argument und kann die double wörtlichen 48.4 die ganzzahligen 48 für den Anruf konvertieren.


Was, warum es verbindet, es ist, weil, wenn diese Funktionen können als Makros implementiert werden, das ist keine Voraussetzung. Was ist eine Anforderung ist, dass diese Funktionen, zumindest in der C11-Standard (ich habe keine ältere Version im Moment verfügbar), müssen sich der aktuellen Gebietsschema bewusst sein, die ihre Umsetzung als Makros viel schwieriger machen wird, und viel einfacher als normale Bibliotheksfunktionen. Und da die Standardbibliothek immer verlinkt ist (es sei denn, Sie teilen GCC anderweitig mit), sind die Funktionen verfügbar.

2

Zunächst einmal #include Aussagen haben nichts mit linking zu tun. Denken Sie daran, alles mit einem in Front in C ist für den Präprozessor, nicht der Compiler oder der Linker gemeint.

Aber das sagte die Funktion muss verknüpft werden, nicht wahr?

Lassen Sie uns die Schritte in separaten Schritten ausführen.

Nun, wie Sie sehen, gcc's Flusen (statische Analysator) ist in Aktion!

Was wird gehen wir es zu ignorieren ...

$ 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)); 

Diesmal nur eine Warnung. Jetzt haben wir eine Objektdatei im aktuellen Verzeichnis. Lassen Sie uns untersuchen Sie ihn ...

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

Wie Sie beide printf und isdigit als undefiniert aufgeführt wird sehen können. Also muss der Code irgendwo herkommen, nicht wahr?

lass uns fortfahren, es zu verbinden ...

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

Nun, wie Sie sehen können, ist die Situation leicht verbessert. Als isdigit und printf sind nicht hilflose Einzelgänger wie sie in der st.o waren. Sie können sehen, dass beide Funktionen von GLIBC_2.2.5 bereitgestellt werden. Aber wo ist das GLIBC?

Nun lassen Sie uns das endgültige ausführbare etwas untersuchen ...

$ 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 ... es ist, dass libc. So stellt es sich heraus, obwohl Sie keine Anweisung gegeben haben, verlinkt der Linker standardmäßig mit 3 Bibliotheken, eine davon ist die libc, die sowohl printf als auch isdigit enthält.

Sie können das Standardverhalten des Linkers sehen von:

$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}} 

Was die beiden anderen Bibliotheken sind?

Gut erinnern, als Sie in a.out gegraben haben, wurden sowohl printf als auch isdigit immer noch als U angezeigt, das bedeutet unbekannt. Mit anderen Worten, es gab keine memory Adresse, die diesen Symbolen zugeordnet ist.

In Wirklichkeit ist dies die Magie. Diese Bibliotheken wurden während der Laufzeit tatsächlich geladen, nicht während der Verbindungszeit wie ältere Systeme.

Wie ist es implementiert? Nun, es hat einen Jargon zugeordnet, so etwas wie faul Verknüpfung. Was es tut, ist, wenn der Prozess eine Funktion aufruft, wenn es keine Speicheradresse (TEXT-Abschnitt) gibt, erzeugt es eine Trap (etwas wie eine Ausnahme im Hochsprachenjargon, wenn die Kontrolle an die Sprachmaschine übergeben wird). Der Kernel fängt diese Trap ab und übergibt sie an den dynamischen Lader, der die Bibliothek lädt und die zugeordnete Speicheradresse an den aufrufenden Prozess zurückgibt.

Es gibt mehrere theoretische Gründe, warum Dinge fauler zu machen, ist besser als vorher. Ich denke, das ist ein ganz neues Thema, das wir zu einem anderen Zeitpunkt diskutieren werden.

Verwandte Themen