Pourquoi devez-vous lier la bibliothèque mathématique en C?

254

Si j'inclus <stdlib.h>ou <stdio.h>dans un programme C, je n'ai pas à les lier lors de la compilation mais je dois me lier à <math.h>, en utilisant -lmavec gcc, par exemple:

gcc test.c -o test -lm

Quelle est la raison pour ça? Pourquoi dois-je lier explicitement la bibliothèque mathématique mais pas les autres bibliothèques?

Nan
la source

Réponses:

249

Les fonctions dans stdlib.het stdio.hont des implémentations dans libc.so(ou libc.apour la liaison statique), qui est liée à votre exécutable par défaut (comme si elle -lcétait spécifiée). GCC peut être chargé d'éviter ce lien automatique avec les options -nostdlibou -nodefaultlibs.

Les fonctions mathématiques de math.hont des implémentations dans libm.so(ou libm.apour la liaison statique) et libmne sont pas liées par défaut. Il y a des raisons historiques à cette séparation libm/ libc, aucune d'elles n'est très convaincante.

Fait intéressant, le runtime C ++ libstdc++nécessite libm, donc si vous compilez un programme C ++ avec GCC ( g++), vous serez automatiquement libmlié.

éphémère
la source
8
Cela n'a rien à voir avec Linux, car c'était courant bien avant Linux. Je soupçonne que cela a quelque chose à voir avec la tentative de minimiser la taille de l'exécutable, car il existe de nombreux programmes qui n'ont pas besoin de fonctions mathématiques.
David Thornley
39
Sur les systèmes anciens, si les fonctions mathématiques étaient contenues dans libc, la compilation de tous les programmes serait plus lente, les exécutables de sortie seraient plus volumineux et l'exécution nécessiterait plus de mémoire, sans aucun avantage pour la plupart des programmes qui n'utilisent pas ces fonctions mathématiques. De nos jours, nous avons un bon support pour les bibliothèques partagées, et même lors de la liaison statique, les bibliothèques standard sont configurées de sorte que le code inutilisé puisse être supprimé, donc aucune de ces raisons n'est plus une bonne.
éphémère
38
@ephemient Même dans l'ancien temps, la liaison à une bibliothèque ne tirait pas tout le contenu de la bibliothèque vers l'exécutable. Les linkers, bien qu'une technologie souvent ignorée, ont toujours été assez efficaces.
7
@ephemient En outre, les bibliothèques partagées existent depuis plus longtemps que vous ne le pensez. Ils ont été inventés dans les années 1950, pas dans les années 1980.
5
Je suppose qu'en fin de compte, ce que nous examinons n'est rien d'autre que du conservatisme du CCG: "ça a toujours fonctionné comme ça". Je souhaite seulement qu'ils appliquent le même raisonnement à leurs extensions de compilateur.
77

N'oubliez pas que C est un ancien langage et que les FPU sont un phénomène relativement récent. J'ai vu C pour la première fois sur des processeurs 8 bits où il y avait beaucoup de travail pour faire de l'arithmétique même sur 32 bits. Un grand nombre de ces mises en œuvre ne même pas avoir une bibliothèque de mathématiques à virgule flottante disponible!

Même sur les 68000 premières machines (Mac, Atari ST, Amiga), les coprocesseurs à virgule flottante étaient souvent des modules complémentaires coûteux.

Pour faire tout ce calcul en virgule flottante, vous aviez besoin d'une bibliothèque assez importante. Et les calculs allaient être lents. Vous avez donc rarement utilisé des flotteurs. Vous avez essayé de tout faire avec des entiers ou des entiers mis à l'échelle. Quand vous avez dû inclure math.h, vous avez serré les dents. Souvent, vous écriviez vos propres approximations et tables de recherche pour l'éviter.

Les compromis existaient depuis longtemps. Parfois, il y avait des packages mathématiques concurrents appelés "fastmath" ou autres. Quelle est la meilleure solution pour les mathématiques? Des trucs vraiment précis mais lents? Imprécis mais rapide? De grandes tables pour les fonctions trigonométriques? Ce n'est que lorsque les coprocesseurs ont été garantis dans l'ordinateur que la plupart des implémentations sont devenues évidentes. J'imagine qu'il y a un programmeur quelque part en ce moment, travaillant sur une puce intégrée, essayant de décider d'apporter la bibliothèque mathématique pour gérer un problème mathématique.

C'est pourquoi les mathématiques n'étaient pas standard . Beaucoup ou peut-être la plupart des programmes n'utilisaient pas un seul flottant. Si les FPU avaient toujours été là et que les flotteurs et les doubles étaient toujours bon marché, il y aurait sans aucun doute eu un "stdmath".

Nosredna
la source
Hé, j'utilise des approximants Pade pour (1 + x) ^ y en Java, sur un PC de bureau. Log, exp et pow sont encore lents.
quant_dev
Bon point. Et j'ai vu des approximations pour sin () dans les plugins audio.
Nosredna
11
Cela explique pourquoi libmn'est pas lié par défaut, mais les mathématiques étaient standard depuis C89 et avant cela, K&R les avait standardisées de facto , donc votre remarque "stdmath" n'a pas de sens.
Fred Foo
@FredFoo Les types et les interfaces ont été standardisés, mais pas les implémentations. Je pense que Nosredna fait référence à une bibliothèque mathématique standard.
Tim Bird
72

À cause d'une pratique historique ridicule que personne ne veut réparer. La consolidation de toutes les fonctions requises par C et POSIX dans un seul fichier de bibliothèque éviterait non seulement de poser cette question à plusieurs reprises, mais permettrait également d'économiser beaucoup de temps et de mémoire lors de la liaison dynamique, car chaque .sofichier lié nécessite les opérations du système de fichiers pour le localiser et le trouver, et quelques pages pour ses variables statiques, délocalisations, etc.

Une mise en œuvre où toutes les fonctions sont dans une bibliothèque et -lm, -lpthread, -lrtoptions , etc. sont tous pas d'habitation (ou lien vers vides .afichiers) est parfaitement conforme POSIX et certainement préférable.

Remarque: je parle de POSIX parce que C lui-même ne spécifie rien sur la façon dont le compilateur est appelé. Ainsi, vous pouvez simplement considérer gcc -std=c99 -lmcomme la manière spécifique à l'implémentation le compilateur doit être appelé pour un comportement conforme.

R .. GitHub ARRÊTEZ D'AIDER LA GLACE
la source
9
+1 pour indiquer que POSIX ne nécessite pas l'existence de bibliothèques libm, libc et librt séparées. Par exemple, sous Mac OS, tout est situé dans un seul libSystem (qui comprend également libdbm, libdl, libgcc_s, libinfo, libm, libpoll, libproc et librpcsvc).
F'x
3
–1 pour spéculer sur l'impact de la recherche de bibliothèque sur les performances sans la sauvegarder avec un lien ou des nombres. "Profil. Ne
spéculez
12
Ce n'est pas de la spéculation. Je n'ai pas d'articles publiés, mais j'ai fait toutes les mesures moi-même et la différence est énorme. Utilisez simplement l' straceune des options de synchronisation pour voir combien de temps de démarrage est consacré à la liaison dynamique, ou comparez l'exécution ./configuresur un système où tous les utilitaires standard sont liés statiquement à ceux où ils sont liés dynamiquement. Même les développeurs d'applications de bureau grand public et les intégrateurs de systèmes sont conscients des coûts de la liaison dynamique; c'est pourquoi des choses comme le pré-lien existent. Je suis sûr que vous pouvez trouver des repères dans certains de ces articles.
R .. GitHub ARRÊTEZ D'AIDER LA GLACE
1
Notez que Posix ne nécessite -lmd'être acceptée et les applications qui utilisent les interfaces mathématiques doivent utiliser -lm, mais il peut être une option interne traitée (ou même ignoré) par la commande du compilateur, pas un fichier de bibliothèque réelle. Ou il peut simplement s'agir d'un .afichier vide si les interfaces sont dans la libc principale.
R .. GitHub ARRÊTEZ D'AIDER LA GLACE
6
@FX: Je ne sais pas pourquoi j'ai oublié de le mentionner auparavant: strace -ttvous montrera facilement le temps passé sur les liens dynamiques. Ce n'est pas joli. Et sous Linux, l'inspection /proc/sys/smapsvous montrera la surcharge de mémoire des bibliothèques supplémentaires.
R .. GitHub STOP HELPING ICE
33

Parce que time()et certaines autres fonctions sont builtindéfinies dans la bibliothèque C ( libc) elle-même et GCC est toujours lié à libc sauf si vous utilisez l' -ffreestandingoption de compilation. Cependant, les fonctions mathématiques existent dans libmlesquelles n'est pas implicitement lié par gcc.

ismail
la source
8
Sur LLVM gcc, je n'ai pas besoin d'ajouter -lm. Pourquoi est-ce?
bot47
26

Une explication est donnée ici :

Donc, si votre programme utilise des fonctions mathématiques et inclut math.h, vous devez lier explicitement la bibliothèque mathématique en passant le -lmdrapeau. La raison de cette séparation particulière est que les mathématiciens sont très pointilleux sur la façon dont leurs mathématiques sont calculées et ils peuvent vouloir utiliser leur propre implémentation des fonctions mathématiques au lieu de l'implémentation standard. Si les fonctions mathématiques étaient regroupées, libc.ail ne serait pas possible de le faire.

[Éditer]

Je ne suis cependant pas sûr d'être d'accord avec cela. Si vous avez une bibliothèque qui fournit, disons, sqrt()et que vous la transmettez avant la bibliothèque standard, un éditeur de liens Unix prendra votre version, non?

Bastien Léonard
la source
10
Je ne pense pas qu'il y ait une garantie que cela se produira; vous pourriez vous retrouver avec un conflit de symboles à la place. Cela dépendrait probablement de l'éditeur de liens et de la disposition de la bibliothèque. Je trouve encore que cette raison est faible; si vous créez une fonction sqrt personnalisée, vous ne devriez vraiment pas lui donner le même nom que la fonction sqrt standard, même si elle fait la même chose ...
ephemient
1
En effet, créer votre propre fonction (non statique) nommée sqrtdonne un programme au comportement indéfini.
R .. GitHub STOP HELPING ICE
@Bastien Bonne trouvaille. Et pour en venir à votre propos, que voulez-vous dire par «avant la bibliothèque standard»? Je pensais que la bibliothèque standard est liée par défaut et n'a pas besoin d'être liée via les options de ligne de commande. Ainsi, la bibliothèque standard sera le premier go-to pour l'éditeur de liens et on ne peut pas placer leur propre implémentation "avant la bibliothèque standard".
Rocky Inde
@RockyInde: regardez ma réponse, je pense que je voulais dire "avant la bibliothèque mathématique standard". Mais je pense qu'il existe des options de compilation pour ne pas lier la bibliothèque C standard, ce qui vous permettrait de passer la vôtre.
Bastien Léonard
@ BastienLéonard J'utilise gcc de la version 7.2, qui -lmest totalement optionnel. Toutes les idées
Donghua Liu
5

Il y a une discussion approfondie sur la liaison à des bibliothèques externes dans Une introduction à GCC - Liaison avec des bibliothèques externes . Si une bibliothèque est membre des bibliothèques standard (comme stdio), vous n'avez pas besoin de spécifier au compilateur (vraiment l'éditeur de liens) pour les lier.

EDIT: Après avoir lu certaines des autres réponses et commentaires, je pense que la référence libc.a et la référence libm qu'elle relie aux deux ont beaucoup à dire sur la raison pour laquelle les deux sont séparés.

Notez que de nombreuses fonctions de 'libm.a' (la bibliothèque mathématique) sont définies dans 'math.h' mais ne sont pas présentes dans libc.a. Certains le sont, ce qui peut prêter à confusion, mais la règle de base est la suivante: la bibliothèque C contient les fonctions qui doivent exister selon ANSI, de sorte que vous n'avez pas besoin du -lm si vous utilisez uniquement des fonctions ANSI. En revanche, `libm.a 'contient plus de fonctions et prend en charge des fonctionnalités supplémentaires telles que le rappel matherr et la conformité à plusieurs normes de comportement alternatives en cas d'erreurs de FP. Voir la section libm, pour plus de détails.

Bill le lézard
la source
1
Ce qui ne répond pas à la question de savoir pourquoi vous devez créer un lien dans les bibliothèques de correspondance séparément. Évidemment, vous voulez avoir à lier séparément les bibliothèques OpenGL, mais sans doute les bibliothèques mathématiques sont généralement utiles.
David Thornley
@David: C'est vrai. Il n'était pas clair pour moi à la question que c'était le morceau que le PO demandait. Je modifiais ma réponse comme vous l'avez dit.
Bill the Lizard
Je connais la raison pour laquelle j'ai compilé un programme qui utilise la sqrtfonction et cela fonctionne sans inclure la bibliothèque via -lm. Merci!
L_K
5

Comme l'éphémient l'a dit, la bibliothèque C libc est liée par défaut et cette bibliothèque contient les implémentations de stdlib.h, stdio.h et plusieurs autres fichiers d'en-tête standard. Juste pour y ajouter, selon " An Introduction to GCC ", la commande de l'éditeur de liens pour un programme de base "Hello World" en C est comme ci-dessous:

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

Notez l'option -lc dans la troisième ligne qui relie la bibliothèque C.

ardsrk
la source
3

Je pense que c'est un peu arbitraire. Vous devez tracer une ligne quelque part (quelles bibliothèques sont par défaut et lesquelles doivent être spécifiées).

Cela vous donne la possibilité de le remplacer par un autre qui a les mêmes fonctions, mais je ne pense pas que ce soit très courant de le faire.

EDIT: (d'après mes propres commentaires): Je pense que gcc fait cela pour maintenir la compatibilité descendante avec le cc d'origine. J'imagine pourquoi cc fait cela à cause du temps de construction - cc a été écrit pour les machines avec beaucoup moins de puissance que nous avons maintenant. De nombreux programmes n'ont pas de calcul en virgule flottante et ils ont probablement pris toutes les bibliothèques qui n'étaient pas couramment utilisées par défaut. Je suppose que le temps de construction du système d'exploitation UNIX et des outils qui l'accompagnaient ont été la force motrice.

Lou Franco
la source
Je pense que la mentalité derrière la question est que le contenu de libm fait largement partie de la bibliothèque C standard, pourquoi ne sont-ils pas dans libc?
Evan Teran
1
La raison pour laquelle gcc est de maintenir la compatibilité avec le cc d'origine dans AT&T Unix. J'ai utilisé des 3B2 en 1988 et il fallait -lm pour faire des maths. Cela me semblait complètement arbitraire à l'époque. Dans Visual Studio, je ne me souviens pas avoir jamais dû ajouter des mathématiques, mais vous devez parfois ajouter d'autres bibliothèques d'exécution en apparence c. Je suppose que les fournisseurs de compilateurs ont une raison (le temps de construction?), Mais pour le moment, je parie que gcc essaie simplement d'être rétrocompatible.
Lou Franco
3

Si je mets stdlib.h ou stdio.h, je n'ai pas à les lier mais je dois les lier quand je compile:

stdlib.h, stdio.hsont les fichiers d'en-tête. Vous les incluez pour votre commodité. Ils prévoient uniquement les symboles qui deviendront disponibles si vous créez un lien dans la bibliothèque appropriée. Les implémentations sont dans les fichiers de la bibliothèque, c'est là que les fonctions vivent vraiment.

L'inclusion math.hn'est que la première étape pour accéder à toutes les fonctions mathématiques.

De plus, vous n'avez pas à vous lier libmsi vous n'utilisez pas ses fonctions, même si vous effectuez une #include <math.h>étape qui n'est qu'une information pour vous, pour le compilateur sur les symboles.

stdlib.h, stdio.hreportez - vous aux fonctions disponibles dans libc, qui se trouvent toujours liées afin que l'utilisateur n'ait pas à le faire lui-même.

Adrian Panasiuk
la source
2

stdio fait partie de la bibliothèque C standard avec laquelle, par défaut, gcc sera lié.

Les implémentations de la fonction mathématique se trouvent dans un fichier libm distinct qui n'est pas lié par défaut, vous devez donc le spécifier -lm. Soit dit en passant, il n'y a aucune relation entre ces fichiers d'en-tête et les fichiers de bibliothèque.


la source
3
il sait que..il demande pourquoi
Evan Teran
Il dit pourquoi. Simon explique que certaines bibliothèques sont liées par défaut, comme stdio alors que la bibliothèque mathématique n'est pas liée par défaut, elle doit donc être spécifiée.
mnuzzo
5
Je dirais que la nature de la question est de savoir pourquoi libm n'est pas lié par défaut (ou même séparé de libc) puisque son contenu fait largement partie de la bibliothèque c standard.
Evan Teran
2

Je suppose que c'est un moyen de rendre les applications qui ne l'utilisent pas du tout légèrement plus performantes. Voici ma réflexion à ce sujet.

Les systèmes d'exploitation x86 (et j'imagine que d'autres) doivent stocker l'état du FPU sur le changement de contexte. Cependant, la plupart des systèmes d'exploitation ne prennent la peine de sauvegarder / restaurer cet état qu'après que l'application a tenté d'utiliser le FPU pour la première fois.

En plus de cela, il y a probablement un code de base dans la bibliothèque mathématique qui mettra le FPU dans un état de base sain lorsque la bibliothèque est chargée.

Donc, si vous ne liez pas du tout de code mathématique, rien de tout cela ne se produira, par conséquent, le système d'exploitation n'a pas du tout à sauvegarder / restaurer l'état FPU, ce qui rend les changements de contexte légèrement plus efficaces.

Mais juste une supposition.

EDIT: en réponse à certains commentaires, la même prémisse de base s'applique toujours aux cas non FPU (la prémisse étant qu'il s'agissait de faire légèrement mieux les applications qui n'utilisaient pas libm).

Par exemple, s'il existe un soft-FPU qui était semblable aux premiers jours de C. Ensuite, avoir libm séparé pourrait empêcher beaucoup de code volumineux (et lent s'il était utilisé) d'être lié inutilement.

De plus, s'il n'y a que des liens statiques disponibles, un argument similaire s'applique, selon lequel les tailles exécutables et les temps de compilation seront réduits.

Evan Teran
la source
Si vous ne liez pas avec libm mais touchez le FPU x87 par d'autres moyens (opérations sur les flotteurs, par exemple), le noyau x86 doit enregistrer l'état du FPU. Je ne pense pas que ce soit une très bonne supposition ...
éphémère
bien sûr, si vous utilisez manuellement le FPU, le noyau devra toujours sauvegarder / restaurer son état. Je disais que si vous ne l'utilisez jamais (y compris en n'utilisant pas libm), il n'aura pas à le faire.
Evan Teran
En réalité, cela peut dépendre très fortement du noyau. La bibliothèque mathématique utilisée par le noyau pourrait avoir une fonction save_FPU_on_switch () qui l'allume, tandis que d'autres détectent simplement si le FPU a été touché.
Earlz
1
Si je me souviens bien, tout le problème est antérieur aux coprocesseurs à virgule flottante, même sur microprocesseurs.
Nosredna
@earlz: l'approche consistant à enregistrer la demande de bibliothèque de mathématiques serait une conception terrible. Et s'ils utilisent le FPU par d'autres moyens? La seule approche sensée (en plus de toujours enregistrer / restaurer) consisterait à détecter l'utilisation, puis à démarrer l'enregistrement / la restauration.
Evan Teran