Risposte:
Le funzioni sono stdlib.h
e stdio.h
hanno implementazioni in libc.so
(o libc.a
per collegamento statico), che è collegato al tuo eseguibile per impostazione predefinita (come se -lc
fosse specificato). GCC può essere incaricato di evitare questo collegamento automatico con le opzioni -nostdlib
o -nodefaultlibs
.
Le funzioni matematiche math.h
hanno implementazioni in libm.so
(o libm.a
per collegamento statico) e libm
non sono collegate per impostazione predefinita. Ci sono ragioni storiche per questo libm
/ libc
scissione, nessuna delle quali molto convincente.
È interessante notare che il runtime C ++ libstdc++
richiede libm
, quindi se si compila un programma C ++ con GCC ( g++
), si verrà automaticamente libm
collegati.
Ricorda che C è un linguaggio antico e che le FPU sono un fenomeno relativamente recente. Ho visto per la prima volta C su processori a 8 bit in cui era molto difficile fare anche l'aritmetica a numeri interi a 32 bit. Molte di queste implementazioni non ha nemmeno hanno una libreria matematica in virgola mobile a disposizione!
Anche sulle prime 68000 macchine (Mac, Atari ST, Amiga), i coprocessori a virgola mobile erano spesso componenti aggiuntivi costosi.
Per fare tutta quella matematica in virgola mobile, avevi bisogno di una libreria abbastanza grande. E la matematica sarebbe stata lenta. Quindi raramente hai usato i galleggianti. Hai provato a fare tutto con numeri interi o interi in scala. Quando hai dovuto includere math.h, hai stretto i denti. Spesso, scrivevi le tue approssimazioni e le tue tabelle di ricerca per evitarlo.
I compromessi esistevano da molto tempo. A volte c'erano pacchetti di matematica concorrenti chiamati "fastmath" o simili. Qual è la migliore soluzione per la matematica? Roba davvero accurata ma lenta? Inesatto ma veloce? Grandi tavoli per le funzioni di trigger? Non è stato garantito fino a quando i coprocessori erano nel computer che la maggior parte delle implementazioni è diventata ovvia. Immagino che ci sia qualche programmatore là fuori da qualche parte in questo momento, che lavora su un chip incorporato, cercando di decidere se portare nella libreria matematica per gestire qualche problema matematico.
Ecco perché la matematica non era standard . Molti o forse la maggior parte dei programmi non utilizzava un singolo float. Se le FPU fossero sempre state presenti e galleggiassero e raddoppiassero fossero sempre a buon mercato per operare, senza dubbio ci sarebbe stato uno "stdmath".
libm
non è collegato per impostazione predefinita, ma la matematica era standard da C89 e prima di allora, K&R lo aveva di fatto standardizzato, quindi la tua osservazione "stdmath" non ha senso.
A causa della ridicola pratica storica che nessuno è disposto a risolvere. Il consolidamento di tutte le funzioni richieste da C e POSIX in un singolo file di libreria non solo eviterebbe di porre ripetutamente questa domanda, ma risparmierebbe anche una quantità significativa di tempo e memoria durante il collegamento dinamico, poiché ogni .so
file collegato richiede le operazioni del filesystem per localizzarlo e trovarlo, e alcune pagine per le sue variabili statiche, rilocazioni, ecc.
Un'implementazione in cui tutte le funzioni sono in una biblioteca e la -lm
, -lpthread
, -lrt
, ecc opzioni sono tutti no-ops (o linkare a vuote .a
file) è perfettamente conforme a POSIX e certamente preferibile.
Nota: sto parlando di POSIX perché C stesso non specifica nulla su come viene invocato il compilatore. Quindi puoi semplicemente considerare gcc -std=c99 -lm
come il modo specifico di implementazione che il compilatore deve essere invocato per un comportamento conforme.
strace
con una delle opzioni di temporizzazione per vedere quanto tempo di avvio viene impiegato per il collegamento dinamico o confrontare l'esecuzione ./configure
su un sistema in cui tutte le utilità standard sono collegate staticamente rispetto a una in cui sono collegate dinamiche. Anche i principali sviluppatori di app desktop e integratori di sistemi sono consapevoli dei costi del collegamento dinamico; ecco perché esistono cose come il prelink. Sono sicuro che puoi trovare benchmark in alcuni di questi documenti.
-lm
di essere accettato e le applicazioni che utilizzano le interfacce di matematica deve utilizzare -lm
, ma può essere un'opzione interna gestita (o addirittura ignorato) da parte del comando del compilatore, non un vero e proprio file di libreria. Oppure può essere solo un .a
file vuoto se le interfacce sono nella libreria principale.
strace -tt
ti mostrerò facilmente il tempo dedicato al collegamento dinamico. Non è carino. E su Linux, il controllo /proc/sys/smaps
ti mostrerà il sovraccarico di memoria di librerie aggiuntive.
Perché time()
e alcune altre funzioni sono builtin
definite nella libreria C ( libc
) stessa e GCC si collega sempre a libc a meno che non si usi l' -ffreestanding
opzione di compilazione. Tuttavia vivono funzioni matematiche in libm
cui non è implicitamente collegato da gcc.
Una spiegazione è data qui :
Quindi, se il tuo programma utilizza funzioni matematiche e incluse
math.h
, devi collegare esplicitamente la libreria matematica passando la-lm
bandiera. Il motivo di questa particolare separazione è che i matematici sono molto esigenti riguardo al modo in cui viene calcolata la loro matematica e potrebbero voler usare la propria implementazione delle funzioni matematiche anziché l'implementazione standard. Se le funzioni matematiche fossero raggruppatelibc.a
, non sarebbe possibile farlo.
[Modificare]
Non sono sicuro di essere d'accordo con questo, però. Se hai una libreria che fornisce, diciamo, sqrt()
e la passi davanti alla libreria standard, un linker Unix prenderà la tua versione, giusto?
sqrt
porta a un programma con un comportamento indefinito.
-lm
è totalmente opzionale. Qualche idea
C'è una discussione approfondita sul collegamento a librerie esterne in An Introduction to GCC - Collegamento con librerie esterne . Se una libreria è un membro delle librerie standard (come stdio), non è necessario specificare al compilatore (in realtà il linker) per collegarle.
EDIT: Dopo aver letto alcune delle altre risposte e commenti, penso che il riferimento libc.a e il riferimento libm a cui si collega entrambi hanno molto da dire sul perché i due sono separati.
Nota che molte delle funzioni in 'libm.a' (la libreria matematica) sono definite in 'math.h' ma non sono presenti in libc.a. Alcuni possono essere fonte di confusione, ma la regola empirica è questa: la libreria C contiene le funzioni che devono essere stabilite da ANSI, quindi non è necessario il -lm se si utilizzano solo funzioni ANSI. Al contrario, `libm.a 'contiene più funzioni e supporta funzionalità aggiuntive come il call-back di matherr e la conformità a diversi standard di comportamento alternativi in caso di errori FP. Vedi la sezione libm, per maggiori dettagli.
sqrt
funzione e funziona senza includere la libreria tramite -lm
. Grazie!
Come detto a breve, la libreria C libc è collegata per impostazione predefinita e questa libreria contiene le implementazioni di stdlib.h, stdio.h e molti altri file di intestazione standard. Solo per aggiungere ad esso, secondo " An Introduction to GCC " il comando linker per un programma "Hello World" di base 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
Notare l'opzione -lc nella terza riga che collega la libreria C.
Penso che sia un po 'arbitrario. Devi tracciare una linea da qualche parte (quali librerie sono predefinite e quali devono essere specificate).
Ti dà l'opportunità di sostituirlo con uno diverso che ha le stesse funzioni, ma non credo sia molto comune farlo.
EDIT: (dai miei commenti): penso che gcc faccia questo per mantenere la retrocompatibilità con la cc originale. La mia ipotesi sul perché cc fa questo è a causa del tempo di costruzione - cc è stato scritto per macchine con molta meno potenza di quanto abbiamo ora. Molti programmi non hanno matematica in virgola mobile e probabilmente hanno preso tutte le librerie che non erano comunemente usate fuori dai valori predefiniti. Immagino che il tempo di costruzione del sistema operativo UNIX e gli strumenti che ne derivano siano stati la forza trainante.
Se inserisco stdlib.h o stdio.h, non devo collegarli ma devo collegarli quando compilo:
stdlib.h
, stdio.h
sono i file di intestazione. Li includi per tua comodità. Prevedono quali simboli saranno disponibili solo se si collega nella libreria corretta. Le implementazioni sono nei file della libreria, è lì che vivono le funzioni.
Includere math.h
è solo il primo passo per ottenere l'accesso a tutte le funzioni matematiche.
Inoltre, non devi collegarti libm
se non usi le sue funzioni, anche se fai un #include <math.h>
passo che è solo un passo informativo per te, per il compilatore sui 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 solo.
stdio fa parte della libreria C standard che, per impostazione predefinita, gcc collegherà.
Le implementazioni della funzione matematica si trovano in un file libm separato a cui non è collegato per impostazione predefinita, quindi è necessario specificarlo -lm. A proposito, non esiste alcuna relazione tra quei file di intestazione e file di libreria.
Mi immagino che sia un modo per rendere le applicazioni che non utilizzano affatto eseguire un po 'meglio. Ecco il mio pensiero su questo.
I sistemi operativi x86 (e immagino che altri) debbano memorizzare lo stato FPU sul cambio di contesto. Tuttavia, la maggior parte dei sistemi operativi si preoccupa solo di salvare / ripristinare questo stato dopo che l'app ha tentato di utilizzare la FPU per la prima volta.
Inoltre, nella libreria matematica è probabilmente presente un codice di base che imposta la FPU su uno stato di base sano quando la libreria viene caricata.
Quindi, se non si collega alcun codice matematico, nulla 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 quello di rendere le app che non utilizzavano libm funzionavano leggermente meglio).
Ad esempio, se esiste una soft-FPU che era molto simile agli inizi di C. Quindi avere libm separato potrebbe impedire a un sacco di codice di grandi dimensioni (e lento se usato) di collegarsi inutilmente.
Inoltre, se è disponibile solo il collegamento statico, si applica un argomento simile che manterrà le dimensioni eseguibili e i tempi di compilazione inferiori.