Answers:
在功能stdlib.h
和stdio.h
在实现libc.so
(或libc.a
静态链接),它在默认情况下链接到你的可执行文件(好像-lc
被指定)。可以指示GCC避免使用-nostdlib
或-nodefaultlibs
选项进行此自动链接。
中的数学函数math.h
具有的实现libm.so
(或libm.a
用于静态链接),libm
默认情况下未链接。这种libm
/ libc
分裂有历史原因,但没有一个非常令人信服。
有趣的是,C ++运行时libstdc++
需要libm
,因此,如果您使用GCC(g++
)编译C ++程序,则将自动获得libm
链接。
请记住,C是一门古老的语言,FPU是相对较新的现象。我首先在8位处理器上看到C,即使在32位整数算术上也要做很多工作。其中许多实现甚至都没有可用的浮点数学库!
即使在最初的68000台计算机(Mac,Atari ST,Amiga)上,浮点协处理器也常常是昂贵的附件。
要进行所有浮点数学运算,您需要一个相当大的库。数学将变得缓慢。因此,您很少使用浮子。您尝试使用整数或可缩放整数进行所有操作。当您必须包括math.h时,您会咬牙切齿。通常,您会编写自己的近似值和查找表来避免这种情况。
权衡存在很长时间。有时会有一些竞争性的数学软件包,称为“ fastmath”等。什么是数学的最佳解决方案?真的很准确但是很慢的东西?不准确但是很快?触发函数的大表?直到保证协处理器位于计算机中,大多数实现才变得显而易见。我想象现在有某个程序员在某个嵌入式芯片上工作,试图决定是否引入数学库来处理一些数学问题。
这就是为什么数学不是标准的原因。许多甚至大多数程序都没有使用单个浮点数。如果FPU一直存在,并且浮动和双倍交易总是便宜的话,那么无疑会有一个“ stdmath”。
libm
默认情况下没有链接,但是数学是C89的标准功能,在此之前,K&R 实际上已经对其进行了标准化,因此您的“ stdmath”标记没有任何意义。
由于荒谬的历史实践,没有人愿意解决。将C和POSIX所需的所有功能整合到一个库文件中,不仅可以避免一遍又一遍地询问此问题,而且在动态链接时还可以节省大量的时间和内存,因为.so
链接的每个文件都需要文件系统操作找到并找到它,还有几页显示其静态变量,重定位等。
所有功能都在一个图书馆和一个实现-lm
,-lpthread
,-lrt
等选项都是无操作(或链接到空.a
文件)是完全符合的POSIX,当然最好。
注意:我说的是POSIX,因为C本身未指定有关如何调用编译器的任何内容。因此,您可以将其gcc -std=c99 -lm
视为特定于实现的方式,必须将编译器调用以实现一致的行为。
strace
一种计时选项来观察在动态链接上花费了多少启动时间,或者比较./configure
在所有标准实用程序都是静态链接的系统与动态链接的系统上运行的时间。甚至主流的桌面应用程序开发人员和系统集成商都知道动态链接的成本。这就是为什么存在预链接之类的原因。我相信您可以在其中一些论文中找到基准。
-lm
被接受,并且使用math接口的应用程序必须使用-lm
,但是它可以是由编译器命令处理(甚至忽略)的内部选项,而不是实际的库文件。或者,.a
如果接口位于主libc中,则它可能只是一个空文件。
strace -tt
会很容易地向您显示在动态链接上花费的时间。不好看 在Linux上,检查/proc/sys/smaps
将显示其他库的内存开销。
因为time()
和其他一些函数builtin
在C库(libc
)本身中定义,并且GCC 始终链接到libc,除非您使用-ffreestanding
compile选项。但是,数学函数libm
仍然存在,而gcc并不隐式链接它们。
这里给出一个解释:
因此,如果您的程序使用的是数学函数并包含
math.h
,则您需要通过传递-lm
标记来显式链接数学库。进行这种特殊分离的原因是,数学家对数学的计算方式非常挑剔,他们可能希望使用自己的数学函数实现而不是标准实现。如果将数学函数混入libc.a
其中,将无法做到这一点。
[编辑]
不过,我不确定我是否同意。如果您有一个提供的库sqrt()
,并且在标准库之前传递了它,那么Unix链接器将使用您的版本,对吗?
sqrt
导致程序具有未定义的行为。
-lm
是7.2版的gcc,它是完全可选的。任何想法
在GCC简介-与外部库的链接中,有关于对外部库的链接的详尽讨论。如果一个库是标准库的成员(如stdio),则无需指定编译器(实际上是链接器)来链接它们。
编辑:在阅读了其他一些答案和评论后,我认为libc.a引用和链接到两者的libm引用中有很多要说为什么两者分开。
请注意,“ libm.a”(数学库)中的许多函数在“ math.h”中定义,但在libc.a中不存在。有些可能会造成混淆,但是经验法则是这样-C库包含ANSI指示必须存在的那些函数,因此,如果仅使用ANSI函数,则不需要-lm。相反,'libm.a'包含更多功能并支持其他功能,例如MATHERR回调以及在FP错误的情况下遵守几种替代行为标准。有关更多详细信息,请参见libm部分。
sqrt
函数并且函数可以在不包含via的库的情况下工作-lm
。谢谢!
正如临时人员所说,C库libc默认情况下是链接的,并且该库包含stdlib.h,stdio.h和其他几个标准头文件的实现。只是要添加它,根据“ GCC简介 ”,用于C语言中基本“ Hello World”程序的链接器命令如下:
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
注意链接C库的第三行中的-lc选项。
我认为这是任意的。您必须在某处画一条线(哪些是默认库,哪些需要指定)。
它使您有机会用功能相同的另一种替代它,但是我认为这样做不是很普遍。
编辑:(从我自己的评论):我认为gcc这样做是为了保持与原始cc的向后兼容性。我对cc为什么要这样做的猜测是由于构建时间-cc是为功率比现在少得多的机器编写的。许多程序没有浮点数学运算,并且它们可能会将默认情况下不常用的每个库都使用了。我猜想UNIX OS的构建时间及其附带的工具是推动力。
如果我输入stdlib.h或stdio.h,则不必链接它们,但在编译时必须链接:
stdlib.h
, stdio.h
是头文件。为了方便起见,将它们包括在内。他们仅预测在链接到正确的库后将提供哪些符号。实现位于库文件中,这是功能的真正所在。
包含 math.h
仅仅是获得对所有数学功能的访问的第一步。
同样,libm
即使您不使用它的功能,也不必链接,即使您这样做#include <math.h>
,对于您的符号编译器来说,这只是一个信息性步骤。
stdlib.h
,stdio.h
指的是中可用的功能libc
,这些功能恰好总是被链接在一起,因此用户不必自己做。
stdio是标准C库的一部分,默认情况下,gcc将链接到该库。
数学函数的实现位于单独的libm文件中,默认情况下未链接到该文件,因此必须将其指定为-lm。顺便说一下,这些头文件和库文件之间没有关系。
我想这是一种使根本不使用它的应用程序性能更好的方法。这是我的想法。
x86 OS(以及我想象的其他OS)需要在上下文切换中存储FPU状态。但是,大多数OS仅在应用程序首次尝试使用FPU之后才费心保存/恢复此状态。
除此之外,数学库中可能还包含一些基本代码,这些基本代码会在加载库时将FPU设置为基本状态。
因此,如果您根本不链接任何数学代码,则不会发生任何事情,因此,操作系统根本不必保存/恢复任何FPU状态,从而使上下文切换效率更高。
虽然只是一个猜测。
编辑:作为对某些评论的回应,相同的基本前提仍然适用于非FPU情况(前提是要使不使用libm的应用程序的性能稍好一些)。
例如,如果有一个软FPU在C的早期是likley。那么将libm分开可以防止不必要地链接很多大型(如果使用的话,速度很慢)代码。
另外,如果只有静态链接可用,那么将应用类似的论点,即它将减小可执行文件的大小并缩短编译时间。