Domanda

Se includo <stdlib.h> O <stdio.h> in un programma C non devo collegarli durante la compilazione ma devo collegarli a <math.h>, utilizzando -lm con gcc, ad esempio:

gcc test.c -o test -lm

Qual è la ragione di ciò?Perché devo collegare esplicitamente la libreria matematica ma non le altre librerie?

È stato utile?

Soluzione

Le funzioni in stdlib.h e stdio.h hanno implementazioni in libc.so (o libc.a per il 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 in math.h hanno implementazioni in libm.so (o libm.a per il collegamento statico) e libm non è collegato per impostazione predefinita. Ci sono ragioni storiche per questa divisione libc / libstdc++, nessuna delle quali molto convincente.

È interessante notare che il runtime C ++ g++ richiede <=>, quindi se compili un programma C ++ con GCC (<=>), otterrai automaticamente <=> collegato.

Altri suggerimenti

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 avevano nemmeno disponibile una libreria matematica in virgola mobile!

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

Per fare tutto quel calcolo 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 in scala. Quando hai dovuto includere math.h, hai stretto i denti. Spesso, dovresti scrivere le tue approssimazioni e le tue tabelle di ricerca per evitarlo.

I compromessi sono esistiti per molto tempo. A volte c'erano pacchetti di matematica concorrenti chiamati & Quot; fastmath & Quot; o tale. 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, provando a 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 in giro e galleggiassero e raddoppiassero fossero sempre a buon mercato per operare, senza dubbio ci sarebbe stata una & Quot; stdmath & Quot ;.

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 che questa domanda venga posta più e più volte, 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 libreria e le opzioni -lm, -lpthread, -lrt, ecc. sono tutte vietate (o collegate a file .a vuoti) è perfettamente conforme a POSIX e sicuramente 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.

Poiché time() e alcune altre funzioni sono builtin definite nella libreria C (libc) stessa e GCC sempre si collega a libc a meno che non si usi -ffreestanding opzione di compilazione. Tuttavia, le funzioni matematiche vivono in libm che non è implicitamente collegato da gcc.

Viene fornita una spiegazione qui :

  

Quindi, se il tuo programma utilizza funzioni matematiche e include math.h, allora devi collegare esplicitamente la libreria matematica passando il flag -lm. 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 raggruppate in libc.a non sarebbe possibile farlo.

[Modifica]

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?

Come detto ephemient, 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 aggiungerlo, secondo & Quot; Un'introduzione a GCC <> quot; il comando linker per una quot & di base; Hello World " programma 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

Nota l'opzione -lc nella terza riga che collega la libreria C.

C'è una discussione approfondita sul collegamento a librerie esterne in Un'introduzione a GCC - Collegamento con librerie esterne.Se una libreria è un membro delle librerie standard (come stdio), non è necessario specificare al compilatore (in realtà il linker) di collegarle.

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

Si noti che molte delle funzioni in 'libm.a' (la libreria matematica) sono definite in 'math.h' ma non sono presenti in libc.a.Alcuni lo sono, il che potrebbe creare confusione, ma la regola pratica è questa: la libreria C contiene quelle funzioni che secondo ANSI devono esistere, in modo che non sia necessario -lm se si utilizzano solo funzioni ANSI.Al contrario, `libm.a' contiene più funzioni e supporta funzionalità aggiuntive come il callback matherr e la conformità a diversi standard di comportamento alternativi in ​​caso di errori FP.Vedi la sezione libm per maggiori dettagli.

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 una potenza molto inferiore a quella che 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 si trovano 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 a libm se non usi le sue funzioni, anche se fai un #include <math.h> che è solo un passaggio informativo per te, per il compilatore sui simboli.

libc, <=> si riferiscono alle funzioni disponibili in <=>, 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à contro.

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.

Vorrei indovinare che è un modo per fare in modo che le app che non la utilizzano affatto funzionino leggermente 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.

Oltre a questo, probabilmente c'è qualche codice di base nella libreria matematica che imposterà 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 si trattava di fare in modo che le app che non facevano uso di libm funzionassero leggermente migliore).

Ad esempio, se esiste una soft-FPU che era 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 un collegamento statico, si applica un argomento simile che manterrà le dimensioni eseguibili e i tempi di compilazione bassi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top