为什么你必须链接C中的math库?
如果在C程序中包含<stdlib.h>
或<stdio.h>
,那么在编译时就不必将它们链接起来,但是我必须使用-lm
和gcc链接到<math.h>
,例如:
gcc test.c -o test -lm
这是什么原因? 为什么我必须明确地链接math库,而不是其他库?
stdlib.h
和stdio.h
的函数在libc.so
(或静态链接的libc.a
)中有实现,缺省情况下链接到您的可执行文件(如指定了-lc
)。 可以指示GCC避免使用-nostdlib
或-nodefaultlibs
选项自动链接。
math.h
的math函数在libm.so
(或静态链接的libm.a
)中具有实现,缺省情况下libm
未链接。 这个libm
/ libc
分裂有历史的原因,没有一个非常有说服力。
有趣的是,C ++运行库libstdc++
需要libm
,所以如果你使用GCC( g++
)编译C ++程序,你将自动获得libm
链接。
请记住,C是一种古老的语言,FPU是一个相对较新的现象。 我首先在8位处理器上看到了C,即使是32位的整数算术也需要很多工作。 许多这些实现甚至没有可用的浮点math库!
即使在第一台68000台机器(Mac,Atari ST,Amiga)上,浮点协处理器也经常是昂贵的附加设备。
要做所有浮点math,你需要一个相当大的库。 math会变得缓慢。 所以你很less使用花车。 你试图用整数或缩放整数来做所有事情。 当你不得不包含math.h时,你咬紧牙关。 通常,你会写自己的近似值和查找表来避免它。
权衡存在很长一段时间。 有时候会有一些叫做“fastmath”的竞争性math包。 math的最佳解决scheme是什么? 真的准确但缓慢的东西? 不准确但快速? 三angular函数的大表? 直到协处理器被保证在计算机中,大多数实现变得明显了。 我想,现在有一些程序员正在使用embedded式芯片,试图决定是否引入math库来处理一些math问题。
这就是math不是标准的原因。 很多或者大多数程序没有使用一个浮点数。 如果FPU一直在附近漂浮,双打总是便宜的,毫无疑问会有一个“stdmath”。
这里给出一个解释:
所以如果你的程序正在使用math函数,包括
math.h
,那么你需要通过传递-lm
标志来显式地连接math库。 这种特殊分离的原因是math家对他们的math计算方式非常挑剔,他们可能想用自己的math函数来实现,而不是使用标准的实现。 如果math函数被集成到libc.a
则不可能这样做。
[编辑]
虽然我不确定我是否同意这一点。 如果你有一个提供sqrt()
的库,并且把它传递给标准库之前,Unix链接器会把你的版本,对吧?
正如ephemient所说,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 。
stdio是标准C库的一部分,默认情况下,gcc将链接它。
math函数的实现是在一个单独的libm文件中,默认情况下是不链接的,所以你必须指定它-lm。 顺便说一下,这些头文件和库文件之间没有关系。
我认为这是一种任意的 您必须在某处绘制一条线(哪些库是默认的,哪些库需要指定)。
它让你有机会用一个具有相同function的另一个replace它,但我认为这样做是不常见的。
编辑:(从我自己的意见):我认为海湾合作委员会这样做,以保持与原来的cc的向后兼容性。 我为什么要这样做的原因是因为编译时间 – cc是为现在比现在less得多的机器编写的。 很多程序都没有浮点math,他们可能把每一个在默认情况下都不常用的库。 我猜测UNIX操作系统的编译时间和与之相关的工具是驱动力。
在“GCC介绍 – 与外部库链接”中有关于链接到外部库的详细讨论。 如果一个库是标准库的成员(如stdio),那么你不需要指定编译器(真正的链接器)来链接它们。
编辑:读了一些其他的答案和评论后,我认为libc.a引用和它链接到两者的libm引用有很多关于为什么两者是分开的说。
请注意,“libm.a”(math库)中的许多函数都在“math.h”中定义,但不在libc.a中。 有些是可能会让人困惑的,但是经验法则是这样的–C库包含ANSI规定的必须存在的那些函数,所以如果只使用ANSI函数,则不需要-lm。 相比之下,`libm.a'包含更多的function,并且支持附加的function,例如matherrcallback以及在FP错误的情况下符合几种行为标准。 有关更多详细信息,请参阅libm部分。
如果我把stdlib.h或stdio.h,我不必链接,但我必须链接,当我编译:
stdlib.h
, stdio.h
是头文件。 你包括他们为了您的方便。 他们只是预测什么样的符号将可用,如果你链接在适当的库。 这些实现在库文件中,这是函数的真正存在。
包括math.h
只是获得所有math函数的第一步。
另外,如果你不使用它的函数,即使你做了一个#include <math.h>
这个只是你的一个信息步骤,编译器关于这个符号,你也不需要链接到libm
。
stdlib.h
, stdio.h
指的是libc
可用的函数,这个函数总是被链接在一起,以便用户不必亲自去做。
我猜测这是一种使不使用它的应用程序稍微好一些的方法。 这是我的想法。
x86操作系统(我想其他)需要在上下文切换中存储FPU状态。 然而,大多数操作系统只在第一次尝试使用FPU之后才打算保存/恢复这个状态。
除此之外,math库中可能还有一些基本的代码,在加载库时将FPU设置为一个合理的基本状态。
所以,如果你根本不链接任何math代码,那么这些都不会发生,因此操作系统根本不必保存/恢复任何FPU状态,使得上下文切换稍微有效一些。
只是一个猜测。
编辑:在回应一些评论,同样的基础前提仍然适用于非FPU的情况下(前提是它使没有使用libm的应用程序执行略好)。
例如,如果在C的早期有一个软FPU,那么在libm分离的情况下,可以防止不必要地被链接的很多大的(如果使用的话,速度慢)代码。
另外,如果只有静态链接可用,则类似的参数适用于保持可执行文件大小和编译时间下降。