2009-06-23 14 views
201

Se includo <stdlib.h> o <stdio.h> in un programma C non dover collegare questi durante la compilazione, ma devo collegare a <math.h>, utilizzando -lm con gcc, per esempio:Perché devi collegare la libreria matematica in C?

gcc test.c -o test -lm 

Qual è la ragione per questo? Perché devo collegare esplicitamente la libreria matematica ma non le altre librerie?

risposta

188

Le funzioni in stdlib.h e stdio.h hanno implementazioni in libc.so (o libc.a per collegamento statico), che è collegato al tuo eseguibile di default (come se fosse -lc specificato). GCC può essere istruito per evitare questo collegamento automatico con le opzioni -nostdlib o -nodefaultlibs.

Le funzioni matematiche in math.h hanno implementazioni in libm.so (o libm.a per collegamento statico), e libm non sia collegata in per impostazione predefinita. Esistono motivi storici per questa suddivisione libm/libc, nessuno dei quali molto convincente.

È interessante notare che il runtime libstdc++ C++ richiede libm, quindi se si compila un programma C++ con GCC (g++), si verrà automaticamente ottenere libm collegato in.

+0

OK, quindi questa diventa una domanda di progettazione della libreria - perché le librerie sono partizionate in questo modo? –

+1

Scommetto di ottimizzare i tempi di compilazione di UNIX stesso (e il set di strumenti che è andato di pari passo con esso). A quel tempo, era probabilmente la cosa più complessa che era stata costruita in C. –

+0

niente a che fare con l'emulazione di fpu? ma per Linux l'eventuale emulazione è nel kernel e non nella libm .. giusto? –

3

stdio fa parte della libreria C standard che, per impostazione predefinita, verrà collegata a gcc.

Le implementazioni della funzione matematica si trovano in un file libm separato che non è collegato per impostazione predefinita, quindi è necessario specificarlo -lm. A proposito, non esiste alcuna relazione tra questi file di intestazione e file di libreria.

+2

sa che ... chiede * perché * –

+1

Questo non spiega "perché"? –

+0

Dice perché. Simon spiega che alcune librerie sono collegate per impostazione predefinita, come stdio, mentre la libreria matematica non è collegata per impostazione predefinita, quindi deve essere specificata. – mnuzzo

3

Penso che sia un po 'arbitrario. Devi disegnare una linea da qualche parte (quali librerie sono predefinite e quali devono essere specificate).

Ti dà la possibilità di sostituirlo con un altro che ha le stesse funzioni, ma non penso che sia molto comune farlo.

MODIFICA: (dai miei commenti): Penso che gcc lo faccia per mantenere la retrocompatibilità con il cc originale. La mia ipotesi sul perché cc lo fa a causa del tempo di costruzione - cc è stato scritto per macchine con molto meno potere di quello che abbiamo ora. Molti programmi non hanno calcoli matematici a virgola mobile e probabilmente hanno preso tutte le librerie che non erano comunemente utilizzate al di fuori del default. Immagino che il tempo di costruzione del sistema operativo UNIX e gli strumenti che lo accompagnano siano stati la forza trainante.

+0

Penso che la mentalità dietro la domanda sia che i contenuti di libm sono in gran parte parte della libreria C standard, perché non sono in libc? –

+1

Il motivo per cui gcc è mantenere la compatibilità con il cc originale in AT & T Unix. Ho usato 3B2 nel 1988 e tu hai dovuto -lm per ottenere la matematica. All'epoca mi sembrava del tutto arbitrario. In Visual Studio, non ricordo di dover aggiungere matematica, ma a volte devi aggiungere altre librerie di runtime apparentemente c. Suppongo che i produttori di compilatori abbiano una ragione (tempo di compilazione?), Ma in questo momento, scommetto che gcc sta solo cercando di essere retrocompatibile. –

2

Vorrei indovinare che è un modo per rendere leggermente migliori le app che non la usano affatto. Ecco il mio pensiero su questo.

I sistemi operativi x86 (e immagino altri) devono memorizzare lo stato della FPU sull'interruttore di contesto. Tuttavia, la maggior parte dei sistemi operativi si preoccupa solo di salvare/ripristinare questo stato dopo che l'app tenta di utilizzare la FPU per la prima volta.

In aggiunta a questo, c'è probabilmente un codice di base nella libreria matematica che imposterà la FPU in uno stato di base sensato quando la libreria viene caricata.

Quindi, se non si collega affatto alcun codice matematico, niente di tutto ciò accadrà, quindi il sistema operativo non deve salvare/ripristinare alcuno stato FPU, rendendo gli switch di contesto leggermente più efficienti.

Solo un'ipotesi però.

EDIT: in risposta ad alcuni dei commenti, la stessa premessa di base si applica ancora ai casi non FPU (la premessa è che era di rendere le applicazioni che non si avvalgono libm eseguire un po 'meglio). Ad esempio, se c'è una FPU soft che era simile ai primi giorni di C. Quindi avere libm separato potrebbe impedire di inserire un codice molto grande (e lento se usato) da inutilmente collegato.

Inoltre, se è disponibile solo il collegamento statico, si applica un argomento simile che manterrebbe le dimensioni eseguibili e i tempi di compilazione.

+0

Se non si collega con libm ma si tocca la FPU x87 con altri mezzi (operazioni su flottanti, ad esempio), il kernel x86 deve salvare lo stato della FPU. Non penso che questa sia una buona ipotesi ... – ephemient

+0

ovviamente se usi la FPU manualmente il kernel avrà ancora bisogno di salvare/ripristinare il suo stato. Stavo dicendo che se non lo usi mai (incluso non usare libm) allora non ci sarà bisogno di farlo. –

+0

In realtà può dipendere molto dal kernel. La libreria matematica utilizzata dal kernel potrebbe avere una funzione save_FPU_on_switch() che la attiva, mentre altri rilevano solo se l'FPU è stata toccata. – Earlz

4

Esiste una discussione approfondita sul collegamento alle librerie esterne in An Introduction to GCC - Linking with external libraries. Se una libreria è un membro delle librerie standard (come stdio), non è necessario specificare il compilatore (in realtà il linker) per collegarle.

EDIT: Dopo aver letto alcune delle altre risposte e commenti, penso che il libc.a reference e il riferimento di libm che collega ad entrambi hanno molto da dire sul motivo per cui i due sono separati.

Si noti che molte delle funzioni di 'libm.a' (la libreria matematica) sono definiti 'math.h', ma non sono presenti in libc.a. Alcuni sono, che potrebbero confondersi, ma la regola generale è questa: la libreria C contiene quelle funzioni che i dettati ANSI devono esistere, quindi non è necessario il -lm se si utilizzano solo funzioni ANSI. Al contrario, `libm.a 'contiene più funzioni e supporta funzionalità aggiuntive come la richiamata matherr e la conformità a diversi standard di comportamento alternativi in ​​caso di errori FP. Vedi la sezione libm, per maggiori dettagli.

+1

Quale non risponde alla domanda del motivo per cui è necessario collegare le librerie di corrispondenze separatamente. Ovviamente si desidera collegare separatamente le librerie OpenGL, ma probabilmente le librerie matematiche sono generalmente utili. –

+0

@David: Giusto. Non mi è stato chiaro dalla domanda che questo era il poco che l'OP stava chiedendo. Stavo modificando la mia risposta mentre commentavi. –

+0

Conosco il motivo per cui ho compilato un programma che utilizza la funzione 'sqrt' e funziona senza includere la libreria tramite' -lm'. Grazie! – huachengzan

3

Se metto stdlib.h o stdio.h , non ho a collegare quelli ma devo collegare quando compilo:

stdlib.h, stdio.h sono i file di intestazione. Li includi per la tua convenienza. Prevedono solo quali simboli saranno disponibili se si collega alla libreria corretta. Le implementazioni sono nei file della libreria, è qui che le funzioni vivono davvero.

Includere math.h è solo il primo passaggio per accedere a tutte le funzioni matematiche.

Inoltre, non è necessario eseguire il collegamento con libm se non si utilizzano le sue funzioni, anche se si fa un #include <math.h> che è solo un passaggio informativo per il compilatore relativo ai simboli.

stdlib.h, stdio.h fare riferimento alle funzioni disponibili in libc, che risulta essere sempre collegato in modo che l'utente non debba farlo da sé.

60

Ricordare che C è un linguaggio vecchio e che le FPU sono un fenomeno relativamente recente. Ho visto per la prima volta C su processori a 8 bit dove era molto lavoro fare anche aritmetica a 32 bit interi. Molte di queste implementazioni non hanno nemmeno una libreria matematica a virgola mobile disponibile!

Anche sulle prime 68000 macchine (Mac, Atari ST, Amiga), i coprocessori a virgola mobile erano spesso costosi add-on.

Per eseguire tutta la matematica in virgola mobile, era necessaria una libreria abbastanza grande. E la matematica sarebbe stata lenta. Quindi hai usato raramente dei galleggianti. Hai provato a fare tutto con numeri interi o interi in scala. Quando hai dovuto includere matematica.h, hai stretto i denti. Spesso, scriveresti le tue approssimazioni e le tabelle di ricerca per evitarlo.

I trade-off esistevano da molto tempo. A volte c'erano pacchetti di matematica in competizione chiamati "fastmath" o simili. Qual è la migliore soluzione per la matematica? Cose davvero accurate ma lente? Impreciso ma veloce? Grandi tabelle per le funzioni trigonometriche? Non è stato fino a quando i coprocessori sono stati garantiti nel computer che la maggior parte delle implementazioni è diventato evidente. Immagino che ci sia qualche programmatore là fuori da qualche parte in questo momento, lavorando su un chip embedded, cercando di decidere se portare la libreria matematica per gestire qualche problema di matematica.

Ecco perché la matematica non era standard. Molti o forse la maggior parte dei programmi non ha utilizzato un singolo float. Se le FPU fossero sempre state in circolazione e fluttuanti e doppi fossero sempre a buon mercato su cui operare, senza dubbio ci sarebbe stato un "stdmath".

+0

Heh, sto usando le approssimazioni di Pade per (1 + x)^y in Java, in un PC desktop. Log, exp e pow sono ancora lenti. –

+0

Buon punto. E ho visto approssimazioni per sin() nei plugin audio. – Nosredna

+7

Questo spiega perché 'libm' non è collegato in modo predefinito, ma la matematica era * standard * da C89 e prima K & R lo aveva * de facto * standardizzato, quindi la tua osservazione" stdmath "non ha senso. –

5

Come diceva l'effimero, la libreria C libc è collegata per impostazione predefinita e questa libreria contiene le implementazioni di stdlib.h, stdio.h e diversi altri file di intestazione standard. Giusto per aggiungere ad essa, secondo il comando "An Introduction to GCC" linker per un programma di base "Ciao Mondo" in C è il seguente:

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 

Avviso l'opzione -lc nella terza linea che collega la C biblioteca.

25

Una spiegazione è data here:

Così, se il programma utilizza funzioni matematiche e comprendente math.h, allora avete bisogno di collegare esplicitamente la libreria matematica passando la bandiera -lm. La ragione di questa particolare separazione è che i matematici sono molto schizzinosi riguardo al modo in cui viene calcolata la loro matematica e potrebbero voler utilizzare la propria implementazione delle funzioni matematiche invece dell'implementazione standard. Se le funzioni matematiche fossero raggruppate in libc.a, non sarebbe possibile farlo.

[Edit]

non sono sicuro sono d'accordo con questo, però. Se hai una libreria che fornisce, per esempio, sqrt() e la passi prima della libreria standard, un linker Unix prenderà la tua versione, giusto?

+8

Non credo ci sia una garanzia che ciò accada; potresti finire con un conflitto di simboli. Probabilmente dipenderà dal linker e dal layout della libreria. Trovo ancora questa ragione per essere debole; se stai facendo una funzione sqrt personalizzata, non dovresti dare lo stesso nome della funzione sqrt standard, anche se fa la stessa cosa ... – ephemient

+1

In effetti, creare una tua funzione (non statica) chiamata ' sqrt' ha come risultato un programma con un comportamento indefinito. –

+0

@Bastien Buona scoperta. E venendo al tuo punto, cosa intendi con "prima della biblioteca standard"? Ho pensato, la libreria standard è collegata per impostazione predefinita e non è richiesto il collegamento tramite le opzioni della riga di comando. Quindi, la libreria standard sarà il primo go-go per il linker e non è possibile posizionare la propria implementazione "prima della libreria standard". –

Problemi correlati