Warum müssen Sie die Mathematikbibliothek in C verknüpfen?

254

Wenn wir sind <stdlib.h>oder <stdio.h>in einem C - Programm muß ich diese nicht verknüpfen beim Kompilieren , aber ich habe zu Link zu <math.h>verwenden -lmmit gcc, zum Beispiel:

gcc test.c -o test -lm

Was ist der Grund dafür? Warum muss ich die Mathematikbibliothek explizit verknüpfen, aber nicht die anderen Bibliotheken?

Nee
quelle

Antworten:

249

Die Funktionen in stdlib.hund stdio.hhaben Implementierungen in libc.so(oder libc.afür statische Verknüpfungen), die standardmäßig mit Ihrer ausführbaren Datei verknüpft sind (als ob -lcangegeben). GCC kann angewiesen werden, diese automatische Verknüpfung mit den Optionen -nostdliboder zu vermeiden -nodefaultlibs.

Die mathematischen Funktionen in math.hhaben Implementierungen in libm.so(oder libm.afür statische Verknüpfungen) und libmsind standardmäßig nicht verknüpft. Es gibt historische Gründe für diese libm/ libcSpaltung, von denen keiner sehr überzeugend ist.

Interessanterweise libstdc++erfordert die C ++ - Laufzeit. libmWenn Sie also ein C ++ - Programm mit GCC ( g++) kompilieren , werden Sie automatisch libmverlinkt.

kurzlebig
quelle
8
Dies hat nichts mit Linux zu tun, da es lange vor Linux üblich war. Ich vermute, es hat etwas mit dem Versuch zu tun, die Größe der ausführbaren Datei zu minimieren, da es viele Programme gibt, die keine mathematischen Funktionen benötigen.
David Thornley
39
Wenn in alten Systemen mathematische Funktionen in libc enthalten wären, wäre das Kompilieren aller Programme langsamer, die ausführbaren Ausgabedateien wären größer und die Laufzeit würde mehr Speicher erfordern, ohne dass die meisten Programme, die diese mathematischen Funktionen überhaupt nicht verwenden, davon profitieren würden . Heutzutage haben wir eine gute Unterstützung für gemeinsam genutzte Bibliotheken, und selbst wenn statisch verknüpft wird, sind die Standardbibliotheken so eingerichtet, dass nicht verwendeter Code verworfen werden kann, sodass keiner dieser Gründe mehr ein guter Grund ist.
Ephemient
38
@ephemient Selbst in den alten Tagen hat das Verknüpfen mit einer Bibliothek nicht den gesamten Inhalt der Bibliothek in die ausführbare Datei übernommen. Obwohl Linker eine oft ignorierte Technologie sind, waren sie historisch gesehen recht effizient.
7
@ephemient Außerdem gibt es gemeinsam genutzte Bibliotheken schon länger als Sie vielleicht denken. Sie wurden in den 1950er Jahren erfunden, nicht in den 1980er Jahren.
5
Ich nehme an, am Ende des Tages ist das, was wir sehen, nichts anderes als GCC-Konservatismus: "Es hat immer so funktioniert". Ich wünschte nur, sie hätten die gleichen Überlegungen auf ihre Compiler-Erweiterungen angewendet.
77

Denken Sie daran, dass C eine alte Sprache ist und dass FPUs ein relativ junges Phänomen sind. Ich habe C zum ersten Mal auf 8-Bit-Prozessoren gesehen, bei denen es eine Menge Arbeit war, sogar 32-Bit-Ganzzahlarithmetik durchzuführen. Viele dieser Implementierungen verfügten nicht einmal über eine Gleitkomma-Mathematikbibliothek!

Selbst auf den ersten 68000-Maschinen (Mac, Atari ST, Amiga) waren Gleitkomma-Coprozessoren oft teure Add-Ons.

Um all diese Gleitkomma-Berechnungen durchzuführen, brauchten Sie eine ziemlich große Bibliothek. Und die Mathematik würde langsam sein. Sie haben also selten Schwimmer benutzt. Sie haben versucht, alles mit Ganzzahlen oder skalierten Ganzzahlen zu tun. Wenn Sie math.h einbeziehen mussten, haben Sie Ihre Zähne zusammengebissen. Oft schreiben Sie Ihre eigenen Näherungswerte und Nachschlagetabellen, um dies zu vermeiden.

Kompromisse bestanden lange Zeit. Manchmal gab es konkurrierende Mathematikpakete namens "Fastmath" oder so. Was ist die beste Lösung für Mathematik? Wirklich genaues, aber langsames Zeug? Ungenau aber schnell? Große Tabellen für Triggerfunktionen? Erst als garantiert wurde, dass sich Coprozessoren im Computer befinden, wurden die meisten Implementierungen offensichtlich. Ich stelle mir vor, dass es irgendwo irgendwo einen Programmierer gibt, der an einem eingebetteten Chip arbeitet und versucht, zu entscheiden, ob er die Mathematikbibliothek einbindet, um ein mathematisches Problem zu lösen.

Deshalb war Mathe kein Standard . Viele oder vielleicht die meisten Programme verwendeten keinen einzigen Float. Wenn FPUs schon immer da gewesen wären und Floats und Doubles immer billig zu betreiben gewesen wären, hätte es zweifellos einen "Standard" gegeben.

Nosredna
quelle
Heh, ich verwende Pade-Approximanten für (1 + x) ^ y in Java auf einem Desktop-PC. Log, exp und pow sind immer noch langsam.
quant_dev
Guter Punkt. Und ich habe Annäherungen für sin () in Audio-Plugins gesehen.
Nosredna
11
Dies erklärt, warum libmnicht standardmäßig verknüpft ist, aber Mathematik war Standard in C89, und zuvor hatte K & R es de facto standardisiert, sodass Ihre Bemerkung "stdmath" keinen Sinn ergibt.
Fred Foo
@FredFoo Die Typen und Schnittstellen wurden standardisiert, nicht jedoch die Implementierungen. Ich denke, Nosredna bezieht sich auf eine Standard-Mathematikbibliothek.
Tim Bird
72

Wegen der lächerlichen historischen Praxis, die niemand reparieren will. Die Konsolidierung aller für C und POSIX erforderlichen Funktionen in einer einzigen Bibliotheksdatei würde nicht nur verhindern, dass diese Frage immer wieder gestellt wird, sondern auch beim dynamischen Verknüpfen viel Zeit und Speicherplatz sparen, da für jede .soverknüpfte Datei die Dateisystemoperationen erforderlich sind um es zu finden und zu finden, und ein paar Seiten für seine statischen Variablen, Verschiebungen usw.

Eine Implementierung , in der alle Funktionen in einer Bibliothek sind und die -lm, -lpthread, -lrtetc. Optionen sind alle No-ops (oder einen Link zu leeren .aDateien) ist eines der besten POSIX konformes und sicherlich vorzuziehen.

Hinweis: Ich spreche von POSIX, da C selbst nichts darüber angibt, wie der Compiler aufgerufen wird. Daher können Sie nur gcc -std=c99 -lmdie implementierungsspezifische Methode behandeln, mit der der Compiler für konformes Verhalten aufgerufen werden muss.

R .. GitHub HÖREN SIE AUF, EIS ZU HELFEN
quelle
9
+1 für den Hinweis, dass POSIX nicht erfordert, dass getrennte Bibliotheken libm, libc und librt vorhanden sind. Unter Mac OS befindet sich beispielsweise alles in einem einzigen libSystem (das auch libdbm, libdl, libgcc_s, libinfo, libm, libpoll, libproc und librpcsvc enthält).
F'x
3
–1 für Spekulationen über die Auswirkungen der Bibliothekssuche auf die Leistung, ohne sie mit einem Link oder Zahlen zu sichern. "Profil. Spekulieren Sie nicht"
F'x
12
Dies ist keine Spekulation. Ich habe keine veröffentlichten Artikel, aber ich habe alle Messungen selbst durchgeführt und der Unterschied ist riesig. Verwenden Sie einfach straceeine der Timing-Optionen, um zu sehen, wie viel Startzeit für die dynamische Verknüpfung aufgewendet wird, oder vergleichen Sie die Ausführung ./configureauf einem System, auf dem alle Standarddienstprogramme statisch verknüpft sind, mit einem System, auf dem sie dynamisch verknüpft sind. Selbst Mainstream-Entwickler und Systemintegratoren von Desktop-Apps sind sich der Kosten einer dynamischen Verknüpfung bewusst. Aus diesem Grund gibt es Dinge wie Prelink. Ich bin sicher, dass Sie in einigen dieser Artikel Benchmarks finden können.
R .. GitHub STOP HELPING ICE
1
Beachten Sie, dass POSIX nicht erforderlich -lmakzeptiert und Anwendungen, die die mathematischen Schnittstellen verwenden müssen , verwenden -lm, aber es kann eine interne Option (oder sogar ignoriert) vom Compiler Befehl behandelt werden, nicht eine tatsächliche Bibliotheksdatei. Oder es kann nur eine leere .aDatei sein, wenn sich die Schnittstellen in der Hauptbibliothek befinden.
R .. GitHub STOP HELPING ICE
6
@FX: Ich weiß nicht, warum ich das vorher vergessen habe: strace -ttzeigt Ihnen leicht die Zeit, die Sie für die dynamische Verknüpfung aufgewendet haben. Es ist nicht schön. Unter Linux zeigt die Überprüfung /proc/sys/smapsden Speicheraufwand für zusätzliche Bibliotheken.
R .. GitHub STOP HELPING ICE
33

Weil time()und einige andere Funktionen builtinin der C-Bibliothek ( libc) selbst definiert sind und GCC immer mit libc verknüpft ist, es sei denn, Sie verwenden die -ffreestandingKompilierungsoption. Es leben jedoch mathematische Funktionen, libmdie nicht implizit durch gcc verknüpft sind.

ismail
quelle
8
Auf LLVM gcc muss ich -lm nicht hinzufügen. Warum ist das?
Bot47
26

Eine Erklärung wird hier gegeben :

Wenn Ihr Programm also mathematische Funktionen verwendet und einschließt math.h, müssen Sie die mathematische Bibliothek explizit verknüpfen, indem Sie das -lmFlag übergeben. Der Grund für diese besondere Trennung ist, dass Mathematiker sehr wählerisch in Bezug auf die Art und Weise sind, wie ihre Mathematik berechnet wird, und dass sie möglicherweise ihre eigene Implementierung der mathematischen Funktionen anstelle der Standardimplementierung verwenden möchten. Wenn die mathematischen Funktionen zusammengefasst libc.awären, wäre dies nicht möglich.

[Bearbeiten]

Ich bin mir jedoch nicht sicher, ob ich damit einverstanden bin. Wenn Sie eine Bibliothek haben, die beispielsweise bereitstellt, sqrt()und diese vor der Standardbibliothek übergeben, wird Ihre Version von einem Unix-Linker übernommen, oder?

Bastien Léonard
quelle
10
Ich glaube nicht, dass es eine Garantie dafür gibt. Möglicherweise kommt es stattdessen zu einem Symbolkonflikt. Es würde wahrscheinlich vom Linker und dem Layout der Bibliothek abhängen. Ich finde diesen Grund immer noch schwach; Wenn Sie eine benutzerdefinierte SQL-Funktion erstellen, sollten Sie ihr nicht den gleichen Namen wie der Standard-SQL-Funktion geben, auch wenn sie dasselbe tut ...
Ephemient
1
Wenn Sie Ihre eigene Funktion (nicht statisch) benennen, erhalten Sie sqrtein Programm mit undefiniertem Verhalten.
R .. GitHub STOP HELPING ICE
@ Bastien Guter Fund. Und was meinen Sie mit "vor der Standardbibliothek"? Ich dachte, die Standardbibliothek ist standardmäßig verknüpft und muss nicht über Befehlszeilenoptionen verknüpft werden. Die Standardbibliothek ist also die erste Anlaufstelle für den Linker, und man kann ihre eigene Implementierung nicht "vor der Standardbibliothek" platzieren.
Rocky Inde
@RockyInde: Schauen Sie sich meine Antwort an, ich glaube, ich meinte tatsächlich "vor der Standard-Mathematikbibliothek". Aber ich denke, es gibt Compiler-Optionen, um die Standard-C-Bibliothek nicht zu verknüpfen, wodurch Sie Ihre übergeben könnten.
Bastien Léonard
@ BastienLéonard Ich benutze gcc der Version 7.2, die -lmvöllig optional ist. Irgendwelche Ideen
Donghua Liu
5

Eine ausführliche Beschreibung der Verknüpfung mit externen Bibliotheken finden Sie in Eine Einführung in GCC - Verknüpfung mit externen Bibliotheken . Wenn eine Bibliothek Mitglied der Standardbibliotheken ist (wie stdio), müssen Sie dem Compiler (wirklich dem Linker) keine Angaben machen, um sie zu verknüpfen.

EDIT: Nachdem ich einige der anderen Antworten und Kommentare gelesen habe, denke ich, dass die libc.a-Referenz und die libm-Referenz, die mit beiden verknüpft sind, viel darüber zu sagen haben, warum die beiden getrennt sind.

Beachten Sie, dass viele der Funktionen in 'libm.a' (der Mathematikbibliothek) in 'math.h' definiert sind, aber in libc.a nicht vorhanden sind. Einige davon können verwirrend sein, aber die Faustregel lautet: Die C-Bibliothek enthält die Funktionen, die ANSI vorschreibt, damit Sie das -lm nicht benötigen, wenn Sie nur ANSI-Funktionen verwenden. Im Gegensatz dazu enthält libm.a mehr Funktionen und unterstützt zusätzliche Funktionen wie den matherr-Rückruf und die Einhaltung mehrerer alternativer Verhaltensstandards bei FP-Fehlern. Weitere Informationen finden Sie im Abschnitt libm.

Bill die Eidechse
quelle
1
Das beantwortet nicht die Frage, warum Sie die Match-Bibliotheken separat verlinken müssen. Natürlich möchten Sie OpenGL-Bibliotheken separat verknüpfen müssen, aber die mathematischen Bibliotheken sind im Allgemeinen nützlich.
David Thornley
@ David: Richtig. Aus der Frage war mir nicht klar, dass dies das Stück war, nach dem das OP fragte. Ich habe meine Antwort bearbeitet, als Sie kommentierten.
Bill the Lizard
Ich kenne den Grund, warum ich ein Programm kompiliert habe, das die sqrtFunktion verwendet und das funktioniert, ohne die Bibliothek über einzuschließen -lm. Vielen Dank!
L_K
5

Wie Ephemient sagte, ist die C-Bibliothek libc standardmäßig verknüpft und diese Bibliothek enthält die Implementierungen von stdlib.h, stdio.h und mehreren anderen Standard-Header-Dateien. Nur um es hinzuzufügen, laut " Eine Einführung in GCC " ist der Linker-Befehl für ein grundlegendes "Hello World" -Programm in C wie folgt:

ld -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o 
/usr/lib/crti.o /usr/libgcc-lib /i686/3.3.1/crtbegin.o
-L/usr/lib/gcc-lib/i686/3.3.1 hello.o -lgcc -lgcc_eh -lc 
-lgcc -lgcc_eh /usr/lib/gcc-lib/i686/3.3.1/crtend.o /usr/lib/crtn.o

Beachten Sie die Option -lc in der dritten Zeile, die die C-Bibliothek verbindet.

ardsrk
quelle
3

Ich denke, es ist irgendwie willkürlich. Sie müssen irgendwo eine Linie ziehen (welche Bibliotheken sind Standardbibliotheken und welche müssen angegeben werden).

Es gibt Ihnen die Möglichkeit, es durch ein anderes zu ersetzen, das die gleichen Funktionen hat, aber ich denke nicht, dass dies sehr häufig ist.

EDIT: (aus meinen eigenen Kommentaren): Ich denke, gcc tut dies, um die Abwärtskompatibilität mit dem Original-CC aufrechtzuerhalten. Meine Vermutung, warum cc dies tut, liegt an der Bauzeit - cc wurde für Maschinen geschrieben, die weitaus weniger Leistung haben als jetzt. Viele Programme haben keine Gleitkomma-Mathematik und haben wahrscheinlich jede Bibliothek, die normalerweise nicht verwendet wird, aus dem Standard genommen. Ich vermute, dass die Build-Zeit des UNIX-Betriebssystems und die damit verbundenen Tools die treibende Kraft waren.

Lou Franco
quelle
Ich denke, die Mentalität hinter der Frage ist, dass der Inhalt von libm größtenteils Teil der Standard-C-Bibliothek ist. Warum sind sie nicht in libc?
Evan Teran
1
Der Grund für gcc ist die Kompatibilität mit dem Original-CC in AT & T Unix. Ich habe 1988 3B2s benutzt und du musstest -lm, um Mathe zu bekommen. Es schien mir damals völlig willkürlich. In Visual Studio kann ich mich nicht erinnern, jemals Mathematik hinzugefügt zu haben, aber manchmal müssen Sie andere scheinbar c-Runtime-Bibliotheken hinzufügen. Ich gehe davon aus, dass die Compiler-Anbieter einen Grund haben (Build-Zeit?), Aber ich wette, gcc versucht gerade, abwärtskompatibel zu sein.
Lou Franco
3

Wenn ich stdlib.h oder stdio.h setze, muss ich diese nicht verknüpfen, aber ich muss beim Kompilieren verknüpfen:

stdlib.h, stdio.hSind die Header - Dateien. Sie fügen sie für Ihre Bequemlichkeit hinzu. Sie prognostizieren nur, welche Symbole verfügbar werden, wenn Sie in der richtigen Bibliothek verlinken. Die Implementierungen befinden sich in den Bibliotheksdateien, dort leben die Funktionen wirklich.

Das Einschließen math.hist nur der erste Schritt, um Zugriff auf alle mathematischen Funktionen zu erhalten.

Außerdem müssen Sie keine Verknüpfung herstellen, libmwenn Sie die Funktionen nicht verwenden, auch wenn Sie einen #include <math.h>für Sie nur informativen Schritt für den Compiler zu den Symbolen ausführen.

stdlib.h, stdio.hBeziehen sich auf Funktionen , die in libc, die so immer verbunden sein passiert , dass der Benutzer nicht es selbst nicht zu tun.

Adrian Panasiuk
quelle
2

stdio ist Teil der Standard-C-Bibliothek, mit der gcc standardmäßig verknüpft wird.

Die Implementierungen der mathematischen Funktionen befinden sich in einer separaten libm-Datei, mit der standardmäßig nicht verknüpft ist. Sie müssen sie daher -lm angeben. Übrigens gibt es keine Beziehung zwischen diesen Header-Dateien und Bibliotheksdateien.


quelle
3
er weiß das ... er fragt warum
Evan Teran
Er sagt warum. Simon erklärt, dass einige Bibliotheken standardmäßig verknüpft sind, wie z. B. stdio, während die mathematische Bibliothek standardmäßig nicht verknüpft ist und daher angegeben werden muss.
Mnuzzo
5
Ich würde sagen, dass die Art der Frage die Frage ist, warum libm nicht standardmäßig verknüpft ist (oder sogar von libc getrennt ist), da sein Inhalt größtenteils Teil der c-Standardbibliothek ist.
Evan Teran
2

Ich würde vermuten, dass es eine Möglichkeit ist, Apps, die es überhaupt nicht verwenden, etwas leistungsfähiger zu machen. Hier ist mein Denken dazu.

x86-Betriebssysteme (und ich stelle mir andere vor) müssen den FPU-Status beim Kontextwechsel speichern. Die meisten Betriebssysteme müssen diesen Status jedoch erst speichern / wiederherstellen, nachdem die App versucht hat, die FPU zum ersten Mal zu verwenden.

Darüber hinaus enthält die Mathematikbibliothek wahrscheinlich einen grundlegenden Code, der die FPU beim Laden der Bibliothek in einen normalen Basiszustand versetzt.

Wenn Sie also überhaupt keinen mathematischen Code einbinden, geschieht nichts davon. Daher muss das Betriebssystem keinen FPU-Status speichern / wiederherstellen, wodurch die Kontextwechsel etwas effizienter werden.

Nur eine Vermutung.

BEARBEITEN: Antwort auf einige der Kommentare gilt dieselbe Grundvoraussetzung immer noch für Nicht-FPU-Fälle (die Voraussetzung war, dass Apps, bei denen libm nicht verwendet wurde, eine etwas bessere Leistung erzielen).

Wenn es zum Beispiel eine Soft-FPU gibt, die in den frühen Tagen von C ähnlich war. Wenn Sie dann libm separate verwenden, kann verhindert werden, dass viel großer (und langsamer, wenn er verwendet wird) Code unnötig verknüpft wird.

Wenn nur statische Verknüpfungen verfügbar sind, gilt ein ähnliches Argument, dass die Größe der ausführbaren Dateien und die Kompilierungszeiten niedrig gehalten werden.

Evan Teran
quelle
Wenn Sie keine Verbindung zu libm herstellen, sondern die x87-FPU auf andere Weise berühren (z. B. Operationen auf Floats), muss der x86-Kernel den FPU-Status speichern. Ich denke nicht, dass dies eine sehr gute Vermutung ist ...
Ephemient
Wenn Sie die FPU manuell verwenden, muss der Kernel seinen Status natürlich noch speichern / wiederherstellen. Ich sagte, wenn Sie es nie verwenden (einschließlich nicht libm verwenden), dann muss es nicht.
Evan Teran
Wirklich kann es sehr stark vom Kernel abhängen. Die vom Kernel verwendete Mathematikbibliothek kann eine Funktion save_FPU_on_switch () haben, die sie aktiviert, während andere nur erkennen, ob die FPU berührt wurde.
Earlz
1
Wenn ich mich richtig erinnere, liegt das ganze Problem lange vor Gleitkomma-Coprozessoren, selbst wenn sie sich auf Mikroprozessoren befinden.
Nosredna
@earlz: Der Ansatz, die Mathe-Bibliothek anfordern zu lassen, wäre ein schreckliches Design. Was ist, wenn sie die FPU auf andere Weise nutzen? Der einzig vernünftige Ansatz (abgesehen davon, dass immer nur gespeichert / wiederhergestellt wird) besteht darin, die Verwendung zu erkennen und dann mit dem Speichern / Wiederherstellen zu beginnen.
Evan Teran