Question

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 pour ça?Pourquoi dois-je lier explicitement la bibliothèque mathématique mais pas les autres bibliothèques ?

Était-ce utile?

La solution

Les fonctions dans stdlib.h et stdio.h avoir 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é précisés).GCC peut être invité à éviter ce lien automatique avec le -nostdlib ou -nodefaultlibs choix.

Les fonctions mathématiques dans math.h avoir 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 à cela libm/libc divisé, aucun d’entre eux n’est très convaincant.

Fait intéressant, le runtime C++ libstdc++ a besoin libm, donc si vous compilez un programme C++ avec GCC (g++), vous obtiendrez automatiquement libm lié.

Autres conseils

N'oubliez pas que 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 disponible!

Même sur les 68 000 premières machines (Mac, Atari ST, Amiga), les coprocesseurs à virgule flottante étaient souvent des add-ons 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 existait des packages mathématiques concurrents appelés & Quot; fastmath & Quot; ou tel. 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 essaie de décider s'il convient d'introduire la bibliothèque mathématique pour résoudre un problème mathématique.

C'est pourquoi les mathématiques n'étaient pas standard . Beaucoup, voire la plupart des programmes, n’utilisaient pas un seul flottant. Si les FPU avaient toujours existé et que les flotteurs et les doubles étaient toujours peu coûteux, il y aurait sans aucun doute eu un & "; Stdmath &";

.

À 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, car chaque .so fichier lié requiert 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 où les options -lm, -lpthread, -lrt, etc. sont no-ops (ou un lien pour vider les fichiers .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 le moyen spécifique à l’implémentation le compilateur doit être appelé pour un comportement conforme.

Étant donné que time() et certaines autres fonctions sont builtin définies dans la bibliothèque C (libc) elle-même, GCC toujours est lié à libc sauf si vous utilisez . -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 dans libc.a il ne serait pas 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, par exemple, sqrt() et que vous la passez avant la bibliothèque standard, un éditeur de liens Unix prendra votre version, non?

Comme l’a dit ephemient, 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 quelque chose, selon & Quot; Une introduction à GCC " la commande de l'éditeur de liens pour un " Hello World " programme 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.

Il existe une discussion approfondie sur les liens vers 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 le compilateur (en réalité l'éditeur de liens) pour les lier.

EDIT: Après avoir lu certaines des réponses et commentaires, je pense que le libc.a référence et la référence à libm qui est liée aux deux ont beaucoup à dire sur la raison pour laquelle les deux sont séparés.

  

Notez que beaucoup des 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 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 supporte des fonctionnalités supplémentaires telles que le rappel matherr et la conformité à plusieurs normes de comportement alternatives en cas d'erreurs de PF. Voir la section libm pour plus de détails.

Je pense que c'est un peu arbitraire. Vous devez dessiner 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: (d'après mes propres commentaires): Je pense que gcc fait cela pour maintenir une compatibilité ascendante avec le cc d'origine. 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 lors de la compilation:

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.

Inclure math.h n'est que la première étape pour accéder à toutes les fonctions mathématiques.

De plus, vous n'avez pas à faire de lien avec 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 sur les symboles.

libc, <=> font référence aux fonctions disponibles dans <=>, qui sont toujours liées entre elles afin que l'utilisateur ne soit pas obligé de le faire lui-même.

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

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. À propos, il n'y a pas de relation entre ces fichiers d'en-tête et les fichiers de bibliothèque.

Je devrais deviner qu'il s'agisse d'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 d'autres encore, j'imagine) doivent stocker l'état de la FPU sur un commutateur de contexte. Cependant, la plupart des systèmes d’exploitation ne se soucient que de sauvegarder / restaurer cet état après que l’application a tenté d’utiliser le FPU pour la première fois.

De plus, il existe probablement un code de base dans la bibliothèque mathématique qui définira la 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 à enregistrer / restaurer les états de la FPU, ce qui rend les changements de contexte légèrement plus efficaces.

Juste une supposition cependant.

MODIFIER: , en réponse à certains commentaires, le même principe de base s'applique toujours aux cas non-FPU (le principe étant qu'il était destiné à créer des applications qui n'utilisaient pas libm légèrement mieux).

Par exemple, s'il existe une FPU souple qui était similaire dans les premiers jours de C. Ensuite, séparer libm pourrait empêcher 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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top