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 les lier à <math.h>
, en utilisant -lm
avec 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?
Les fonctions dans stdlib.h
et stdio.h
ont des implémentations dans libc.so
(ou libc.a
pour les liens statiques), qui est lié par défaut à votre exécutable (comme si -lc
ont été spécifiés). On peut demander à GCC d’éviter ce lien automatique avec le -nostdlib
ou -nodefaultlibs
Les options.
Les fonctions mathématiques dans math.h
ont des implémentations dans libm.so
(ou libm.a
pour les liens statiques), et libm
n'est pas lié par défaut. Il y a des raisons historiques à cette scission libm
/libc
, aucune n'est très convaincante.
Fait intéressant, le runtime C++ libstdc++
requiert libm
, donc si vous compilez un programme C++ avec GCC (g++
), vous obtiendrez automatiquement libm
lié dans.
Rappelez-vous que le C est une langue ancienne et que les FPU sont un phénomène relativement récent. J'ai d'abord vu C sur des processeurs 8 bits où il était très difficile de faire de l'arithmétique même 32 bits. Beaucoup de ces implémentations n'avaient même pas de bibliothèque mathématique à virgule flottante!
Même sur les 68 000 premières machines (Mac, Atari ST, Amiga), les coprocesseurs à virgule flottante étaient souvent des compléments coûteux.
Pour faire tout ce calcul mathématique en virgule flottante, il vous fallait une bibliothèque assez volumineuse. Et le calcul allait être lent. 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 consultation pour l’éviter.
Les compromis existaient depuis longtemps. Parfois, il y avait des paquets de mathématiques en compétition appelés "fastmath" ou autres. Quelle est la meilleure solution pour les mathématiques? Des choses vraiment précises mais lentes? Imprécis mais rapide? De grandes tables pour les fonctions trigonométriques? Ce n’est que lorsque les coprocesseurs ont été assurés d’être dans l’ordinateur que la plupart des implémentations sont devenues évidentes. J'imagine qu'il y a actuellement quelque part un programmeur qui travaille sur une puce intégrée et qui essaie de décider s'il convient d'introduire la bibliothèque mathématique pour traiter un problème mathématique.
C'est pourquoi les mathématiques n'étaient pas standard . Beaucoup ou peut-être la plupart des programmes n'ont pas utilisé un seul flottant. Si les FPU avaient toujours existé et que les flotteurs et les doubles étaient toujours peu coûteux à exploiter, il y aurait sans aucun doute un "std math".
À 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 que cette question soit posée à maintes reprises, mais permettrait également d’économiser beaucoup de temps et de mémoire lors des liaisons dynamiques, puisque chaque .so
fichier lié nécessite les opérations du système de fichiers pour le localiser et le trouver, ainsi que quelques pages pour ses variables statiques, ses relocalisations, etc.
Une implémentation où toutes les fonctions sont dans une bibliothèque et les options -lm
, -lpthread
, -lrt
, Etc. sont toutes no-ops (ou un lien vers vide .a
] est parfaitement conforme à POSIX et certainement préférable.
Remarque: je parle de POSIX car C ne spécifie rien sur la manière dont le compilateur est appelé. Ainsi, vous pouvez simplement traiter gcc -std=c99 -lm
Comme la manière spécifique à l'implémentation que le compilateur doit être appelé pour un comportement conforme.
Parce que time()
et quelques autres fonctions sont builtin
définies dans la bibliothèque C (libc
) elle-même et GCC toujours des liens vers libc sauf si vous utilisez le -ffreestanding
option de compilation. Cependant, les fonctions mathématiques résident dans libm
qui n'est pas implicitement lié par gcc.
Une explication est donnée ici :
Ainsi, 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 est que les mathématiciens sont très pointilleux quant à la manière dont leurs calculs sont calculés et peuvent 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
, Cela ne serait pas possible.
[Modifier]
Je ne suis pas sûr d'être d'accord avec cela, cependant. Si vous avez une bibliothèque qui fournit, par exemple, sqrt()
, et que vous la transmettez avant la bibliothèque standard, un éditeur de liens Unix prendra votre version, non?
Comme l'a dit éphémient, 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 de plusieurs autres fichiers d'en-tête standard. Pour ajouter à cela, selon " ne 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 existe une discussion approfondie sur la liaison à des bibliothèques externes dans 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 le compilateur (en réalité l'éditeur de liens) pour les lier.
EDIT: Après avoir lu certaines des autres réponses et commentaires, je pense que le référence de libc.a et la référence de libm qui est liée aux deux ont beaucoup à dire sur la raison pour laquelle les deux sont séparés.
Notez que de nombreuses fonctions dans '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 pour lesquelles ANSI doit exister, de sorte que vous n'avez pas besoin de -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 FP. Voir la section libm, pour plus de détails). plus de détails.
Je pense que c'est un peu arbitraire. Vous devez tracer une ligne quelque part (quelles bibliothèques sont les bibliothèques par défaut et lesquelles doivent être spécifiées).
Cela vous donne la possibilité de le remplacer par un autre qui remplit les mêmes fonctions, mais je ne pense pas que ce soit très courant.
EDIT: (de mes propres commentaires): Je pense que gcc fait cela pour maintenir une compatibilité ascendante avec le cc original. Je suppose que la raison en est que c’est à cause du temps de construction - c’est écrit pour des machines beaucoup moins puissantes que ce n’est le cas actuellement. Beaucoup de programmes n'ont pas de maths à virgule flottante et ils ont probablement pris toutes les bibliothèques qui n'étaient pas couramment utilisées par défaut. J'imagine que le temps de construction du système d'exploitation UNIX et les outils qui l'accompagnaient étaient la force motrice.
Si je mets stdlib.h ou stdio.h, je n'ai pas à les lier, mais je dois les lier lorsque 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 seront 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 vivent les fonctions.
Comprenant math.h
n'est que la première étape pour accéder à toutes les fonctions mathématiques.
De plus, vous n'avez pas à lier libm
si vous n'utilisez pas ses fonctions, même si vous faites un #include <math.h>
qui n'est qu'une étape d'information pour vous, pour le compilateur concernant les symboles.
stdlib.h
, stdio.h
fait référence aux fonctions disponibles dans libc
, qui sont toujours liées, de sorte que l’utilisateur n’a pas à le faire lui-même.
stdio fait partie de la bibliothèque standard C avec laquelle gcc se liera par défaut.
Les implémentations de la fonction mathématique se trouvent dans un fichier libm séparé non lié par défaut. Vous devez donc le spécifier -lm. À propos, il n'y a pas de relation entre ces fichiers d'en-tête et les fichiers de bibliothèque.
Je voudrais suppose que c’est un moyen de rendre les applications qui ne l’utilisent pas du tout plus performantes. Voici ma pensée sur ceci.
les systèmes d'exploitation x86 (et d'autres encore, j'imagine) doivent stocker l'état de la FPU lors du changement de contexte. Cependant, la plupart des systèmes d'exploitation ne prennent la peine de sauvegarder/restaurer cet état que lorsque l'application tente d'utiliser le FPU pour la première fois.
En plus de cela, il existe probablement un code de base dans la bibliothèque mathématique qui définira le FPU sur un état de base sain lors du chargement de la bibliothèque.
Ainsi, si vous ne liez aucun code mathématique, rien de tout cela ne se produira. Par conséquent, le système d'exploitation n'a pas besoin de sauvegarder/restaurer l'état d'un FPU, ce qui rend les changements de contexte légèrement plus efficaces.
Juste une supposition cependant.
EDIT: En réponse à certains commentaires, le même principe de base s’applique aux cas non-FPU (le principe étant de faire en sorte que les applications qui ne font pas utiliser libm soient légèrement plus performantes).
Par exemple, s'il existe une FPU souple qui était similaire aux débuts de C. Ensuite, séparer libm empêcherait qu'un grand nombre de codes volumineux (et lents s'il était utilisé) ne soient liés inutilement.
De plus, s'il n'y a que des liaisons statiques disponibles, un argument similaire s'applique: les tailles des exécutables et les temps de compilation seront conservés.