为什么你必须链接C中的math库?

如果在C程序中包含<stdlib.h><stdio.h> ,那么在编译时就不必将它们链接起来,但是我必须使用-lm和gcc链接到<math.h> ,例如:

 gcc test.c -o test -lm 

这是什么原因? 为什么我必须明确地链接math库,而不是其他库?

stdlib.hstdio.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.hstdio.h是头文件。 你包括他们为了您的方便。 他们只是预测什么样的符号将可用,如果你链接在适当的库。 这些实现在库文件中,这是函数的真正存在。

包括math.h只是获得所有math函数的第一步。

另外,如果你不使用它的函数,即使你做了一个#include <math.h>这个只是你的一个信息步骤,编译器关于这个符号,你也不需要链接到libm

stdlib.hstdio.h指的是libc可用的函数,这个函数总是被链接在一起,以便用户不必亲自去做。

猜测这是一种使不使用它的应用程序稍微好一些的方法。 这是我的想法。

x86操作系统(我想其他)需要在上下文切换中存储FPU状态。 然而,大多数操作系统只在第一次尝试使用FPU之后才打算保存/恢复这个状态。

除此之外,math库中可能还有一些基本的代码,在加载库时将FPU设置为一个合理的基本状态。

所以,如果你根本不链接任何math代码,那么这些都不会发生,因此操作系统根本不必保存/恢复任何FPU状态,使得上下文切换稍微有效一些。

只是一个猜测。

编辑:在回应一些评论,同样的基础前提仍然适用于非FPU的情况下(前提是它使没有使用libm的应用程序执行略好)。

例如,如果在C的早期有一个软FPU,那么在libm分离的情况下,可以防止不必要地被链接的很多大的(如果使用的话,速度慢)代码。

另外,如果只有静态链接可用,则类似的参数适用于保持可执行文件大小和编译时间下降。