Pourquoi devez-vous lier la bibliothèque mathématique en C?
Si j'inclus <stdlib.h>
ou <stdio.h>
dans un programme C, Je n'ai pas besoin de les lier lors de la compilation, mais je dois créer un lien vers <math.h>
, en utilisant -lm
avec gcc, par exemple:
gcc test.c -o test -lm
Quelle est la raison? Pourquoi dois-je lier explicitement la bibliothèque mathématique mais pas les autres bibliothèques?
9 réponses
Les fonctions de stdlib.h
et stdio.h
ont des implémentations dans libc.so
(ou libc.a
pour la liaison statique), qui est liée dans votre exécutable par défaut (comme si -lc
était spécifié). GCC peut être chargé d'éviter ce lien automatique avec les options -nostdlib
ou -nodefaultlibs
.
Les fonctions mathématiques dans math.h
ont des implémentations dans libm.so
(ou libm.a
pour la liaison statique), et libm
n'est pas lié par défaut. Il y a des raisons historiques à cela libm
/libc
split, aucun d'entre eux très convaincant.
Fait intéressant, le runtime C++ libstdc++
nécessite libm
, donc si vous compilez un programme C++ avec GCC (g++
), vous obtiendrez automatiquement libm
lié.
Rappelez-vous que C est une ancienne langue et que les FPUs sont un phénomène relativement récent. J'ai d'abord vu C sur les processeurs 8 bits où il y avait beaucoup de travail à faire même l'arithmétique entière 32 bits. Beaucoup de ces implémentations n'avaient même pas une bibliothèque mathématique à virgule flottante disponible!
Même sur les 68000 premières machines (Mac, Atari ST, Amiga), les coprocesseurs à virgule flottante étaient souvent des add-ons coûteux.
Pour faire tout ce calcul en virgule flottante, vous aviez besoin d'une bibliothèque assez importante. Et le calcul a été lente. Donc, vous avez rarement utilisé des flotteurs. Vous avez essayé de tout faire avec des entiers ou des entiers mis à l'échelle. Quand vous deviez inclure les mathématiques.h, vous serrant les dents. Souvent, vous écrivez vos propres approximations et tables de recherche pour l'éviter.
Les compromis existent depuis longtemps. Parfois, il y avait des paquets mathématiques concurrents appelés "fastmath" ou autres. Quelle est la meilleure solution pour les mathématiques? Des choses vraiment précises mais lentes? Inexact mais rapide? Grandes tables pour les fonctions trig? Il 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 s'il faut apporter la bibliothèque de mathématiques pour gérer un problème de mathématiques.
C'est pourquoi les maths n'étaient pas norme. Beaucoup ou peut-être la plupart des programmes n'ont pas utilisé un seul flotteur. Si les FPUs avaient toujours été là et que les flotteurs et les doubles étaient toujours bon marché, non doute qu'il y aurait eu un "stdmath".
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 drapeau-lm
. 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 danslibc.a
, Ce ne serait pas le cas être possible de le faire.
[modifier]
Je ne suis pas sûr d'être d'accord avec cela, cependant. 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, n'est-ce pas?
Comme ephemient 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 ajouter à cela, selon " Une Introduction à GCC " la commande de l'éditeur de liens pour un programme de base "Hello World" en C est la suivante:
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.
Il y a une discussion approfondie sur le lien vers des bibliothèques externes dans Une Introduction à GCC-Linking 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 libc .une référence et la référence libm à laquelle elle est liée ont beaucoup à dire sur la raison pour laquelle les deux sont séparé.
Notez que beaucoup de fonctions dans ' libm.a' (la bibliothèque mathématique) sont définis dans ' math.h', mais ne sont pas présents dans la libc.A. certains le sont, ce qui peut être déroutant, mais la règle générale est la suivante-la bibliothèque C contient les fonctions que la norme ANSI dicte doivent exister, 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 alternatives normes de comportement en cas d'erreurs FP. Voir la section libm, pour plus de détails.
Stdio fait partie de la bibliothèque C standard à laquelle, par défaut, gcc sera lié.
Les implémentations de la fonction math sont dans un fichier libm séparé qui n'est pas lié par défaut, vous devez donc le spécifier - lm. En passant, il n'y a aucun rapport entre ces fichiers d'en-tête et les fichiers de bibliothèque.
Je pense que c'est un peu arbitraire. Vous devez tracer une ligne quelque part (quelles bibliothèques sont par défaut et qui 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 commun.
EDIT: (de mes propres commentaires): je pense que gcc le fait pour maintenir la compatibilité ascendante avec le cc d'origine. Ma conjecture pour pourquoi cc fait cela est à cause du temps de construction - cc a été écrit pour les machines avec beaucoup moins de puissance que nous avons maintenant. Beaucoup de programmes n'ont pas de mathématiques à 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 les outils qui l'accompagnent ont été la force motrice.
Si je mets stdlib.h ou stdio.h, je n'ai pas besoin de lier ceux-ci mais je dois lier quand je compile:
stdlib.h
, stdio.h
sont les fichiers d'en-tête. Vous les incluez pour votre commodité. Ils ne prévoient que les symboles qui deviendront disponibles si vous liez 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.
Y Compris math.h
n'est que la première étape pour accéder à toutes les fonctions mathématiques.
Aussi, vous ne le faites pas avoir à lier contre libm
Si vous n'utilisez pas ses fonctions, même si vous faites un #include <math.h>
qui n'est qu'une étape informative pour vous, pour le compilateur sur les symboles.
stdlib.h
, stdio.h
reportez-vous aux fonctions disponibles dans libc
, qui sont toujours liées pour que l'utilisateur n'ait pas à le faire lui-même.
Je suppose que c'est un moyen de rendre les applications qui ne l'utilisent pas du tout légèrement meilleures. Voici ma façon de penser sur ce sujet.
Les systèmes D'exploitation X86 (et j'imagine que d'autres) doivent stocker L'état FPU sur le commutateur 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 ait 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 définira le FPU à un État de base sain lorsque la Bibliothèque est charger.
Donc, si vous ne liez aucun code mathématique, rien de tout cela ne se produira, donc le système d'exploitation n'a pas à enregistrer/restaurer aucun état FPU, rendant les commutateurs de contexte légèrement plus efficaces.
Juste une supposition cependant.
EDIT: en réponse à certains commentaires, la même prémisse de base s'applique toujours aux cas non-FPU (la prémisse étant que c'était pour rendre les applications qui n'utilisaient pas libm légèrement meilleures).
Par exemple, s'il y a un soft-FPU qui était likley dans les premiers jours de C. alors avoir libm séparé pourrait empêcher beaucoup de code volumineux (et lent s'il était utilisé) d'être inutilement lié.
En outre, s'il n'y a que des liens statiques disponibles, un argument similaire s'applique: il garderait les tailles des exécutables et les temps de compilation.