为什么必须在C中链接数学库?


254

如果在C程序中包含<stdlib.h><stdio.h>包含C程序,则在编译时不必链接这些程序,但必须在gcc中<math.h>使用链接到,-lm例如:

gcc test.c -o test -lm

这是什么原因呢?为什么我必须显式链接数学库,而不是其他库?

Answers:


249

在功能stdlib.hstdio.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链接。


8
这与Linux无关,因为它早在Linux之前就很普遍。我怀疑这与尝试减小可执行文件大小有关,因为很多程序不需要数学函数。
David Thornley,2009年

39
在古老的系统上,如果数学函数包含在libc中,则编译所有程序的速度将变慢,输出可执行文件的大小将变大,并且运行时将需要更多的内存,这对大多数根本不使用这些数学函数的程序没有好处。如今,我们已经对共享库提供了很好的支持,即使是静态链接时,也已经建立了标准库,以便可以丢弃未使用的代码,因此这些都不是很好的理由。
短暂

38
@ephemient即使在过去,链接到库也无法将库的所有内容都提取到可执行文件中。链接器尽管经常被忽略,但在历史上一直非常有效。

7
@ephemient此外,共享库的存在时间比您想象的要长。它们是在1950年代发明的,而不是1980年代发明的。

5
我想归根结底,我们所看到的不过是海湾合作委员会的保守主义:“它总是那样工作”。我只希望他们对编译器扩展应用相同的推理。

77

请记住,C是一门古老的语言,FPU是相对较新的现象。我首先在8位处理器上看到C,即使在32位整数算术上也要做很多工作。其中许多实现甚至没有可用的浮点数学库!

即使在最初的68000台计算机(Mac,Atari ST,Amiga)上,浮点协处理器也常常是昂贵的附件。

要进行所有浮点数学运算,您需要一个相当大的库。数学将变得缓慢。因此,您很少使用浮子。您尝试使用整数或可缩放整数进行所有操作。当您必须包括math.h时,您会咬牙切齿。通常,您会编写自己的近似值和查找表来避免这种情况。

权衡存在很长时间。有时会有一些竞争性的数学软件包,称为“ fastmath”等。什么是数学的最佳解决方案?真的很准确但是很慢的东西?不准确但是很快?触发函数的大表?直到保证协处理器位于计算机中,大多数实现才变得显而易见。我想象现在有某个程序员在某个嵌入式芯片上工作,试图决定是否引入数学库来处理一些数学问题。

这就是为什么数学不是标准的原因。许多甚至大多数程序都没有使用单个浮点数。如果FPU一直存在,并且浮动和双倍交易总是便宜的话,那么无疑会有一个“ stdmath”。


嘿,我在台式机上用Java在(1 + x)^ y中使用Pade近似值。日志,exp和pow仍然很慢。
quant_dev

好点子。而且我已经在音频插件中看到了sin()的近似值。
Nosredna

11
这就解释了为什么libm默认情况下没有链接,但是数学是C89的标准功能,在此之前,K&R 实际上已经对其进行了标准化,因此您的“ stdmath”标记没有任何意义。
弗雷德·富

@FredFoo类型和接口是标准化的,但没有实现。我认为Nosredna指的是标准数学库。
Tim Bird

72

由于荒谬的历史实践,没有人愿意解决。将C和POSIX所需的所有功能整合到一个库文件中,不仅可以避免一遍又一遍地询问此问题,而且在动态链接时还可以节省大量的时间和内存,因为.so链接的每个文件都需要文件系统操作找到并找到它,还有几页显示其静态变量,重定位等。

所有功能都在一个图书馆和一个实现-lm-lpthread-lrt等选项都是无操作(或链接到空.a文件)是完全符合的POSIX,当然最好。

注意:我说的是POSIX,因为C本身未指定有关如何调用编译器的任何内容。因此,您可以将其gcc -std=c99 -lm视为特定于实现的方式,必须将编译器调用以实现一致的行为。


9
+1指出POSIX不需要存在分离的libm,libc和librt库。例如,在Mac OS上,所有内容都位于单个libSystem中(还包括libdbm,libdl,libgcc_s,libinfo,libm,libpoll,libproc和librpcsvc)。
F'x 2011年

3
–1用于推测库查找对性能的影响,而无需使用链接或数字进行备份。“个人资料。不要猜测”
F'x 2011年

12
这不是猜测。我没有任何发表的论文,但是我自己完成了所有测量,并且差异很大。只需使用strace一种计时选项来观察在动态链接上花费了多少启动时间,或者比较./configure在所有标准实用程序都是静态链接的系统与动态链接的系统上运行的时间。甚至主流的桌面应用程序开发人员和系统集成商都知道动态链接的成本。这就是为什么存在预链接之类的原因。我相信您可以在其中一些论文中找到基准。
R .. GitHub停止帮助ICE,

1
请注意,POSIX 确实需要-lm被接受,并且使用math接口的应用程序必须使用-lm,但是它可以是由编译器命令处理(甚至忽略)的内部选项,而不是实际的库文件。或者,.a如果接口位于主libc中,则它可能只是一个空文件。
R .. GitHub停止帮助ICE,

6
@FX:不知道为什么我之前忘了提及此事:strace -tt会很容易地向您显示在动态链接上花费的时间。不好看 在Linux上,检查/proc/sys/smaps将显示其他库的内存开销。
R .. GitHub停止帮助ICE,

33

因为time()和其他一些函数builtin在C库(libc)本身中定义,并且GCC 始终链接到libc,除非您使用-ffreestandingcompile选项。但是,数学函数libm仍然存在,而gcc并不隐式链接它们。


8
在LLVM gcc上,我不必添加-lm。为什么是这样?
bot47 2011年

26

这里给出一个解释:

因此,如果您的程序使用的是数学函数并包含math.h,则您需要通过传递-lm标记来显式链接数学库。进行这种特殊分离的原因是,数学家对数学的计算方式非常挑剔,他们可能希望使用自己的数学函数实现而不是标准实现。如果将数学函数混入libc.a其中,将无法做到这一点。

[编辑]

不过,我不确定我是否同意。如果您有一个提供的库sqrt(),并且在标准库之前传递了它,那么Unix链接器将使用您的版本,对吗?


10
我不认为会发生这种情况。您可能最终会遇到符号冲突。这可能取决于链接程序和库的布局。我仍然发现这个理由很薄弱。如果您要创建自定义sqrt函数,则即使使用相同的功能,也不应使用与标准sqrt函数相同的名称...
ephemient

1
确实,使用自己的函数(非静态)命名会sqrt导致程序具有未定义的行为。
R .. GitHub停止帮助ICE,2010年

@Bastien好找到。接下来,您说“在标准库之前”是什么意思?我认为,标准库默认情况下处于链接状态,不需要通过命令行选项进行链接。因此,标准库将是链接器的第一个入门对象,并且不能在“标准库”之前放置自己的实现。
Rocky Inde 2015年

@RockyInde:看我的回答,我想我的意思是“在标准数学库之前”。但是我认为有一些编译器选项可以不链接标准C库,这将允许您传递自己的C库。
巴斯蒂安·莱昂纳德

@BastienLéonard我使用的-lm是7.2版的gcc,它是完全可选的。任何想法
东华刘

5

GCC简介-与外部库的链接中,有关于对外部库的链接的详尽讨论。如果一个库是标准库的成员(如stdio),则无需指定编译器(实际上是链接器)来链接它们。

编辑:在阅读了其他一些答案和评论后,我认为libc.a引用和链接到两者的libm引用中有很多要说为什么两者分开。

请注意,“ libm.a”(数学库)中的许多函数在“ math.h”中定义,但在libc.a中不存在。有些可能会造成混淆,但是经验法则是这样-C库包含ANSI指示必须存在的那些函数,因此,如果仅使用ANSI函数,则不需要-lm。相反,'libm.a'包含更多功能并支持其他功能,例如MATHERR回调以及在FP错误的情况下遵守几种替代行为标准。有关更多详细信息,请参见libm部分。


1
这不能回答为什么必须分别在匹配库中链接的问题。显然,您需要单独链接OpenGL库,但是可以说数学库通常很有用。
David Thornley,2009年

@David:对。从这个问题上我不清楚,这是OP所要问的问题。如您所言,我正在编辑我的答案。
比尔蜥蜴

我知道我编译使用该程序的程序的原因 sqrt函数并且函数可以在不包含via的库的情况下工作-lm。谢谢!
L_K

5

正如临时人员所说,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选项。


3

我认为这是任意的。您必须在某处画一条线(哪些是默认库,哪些需要指定)。

它使您有机会用功能相同的另一种替代它,但是我认为这样做不是很普遍。

编辑:(从我自己的评论):我认为gcc这样做是为了保持与原始cc的向后兼容性。我对cc为什么要这样做的猜测是由于构建时间-cc是为功率比现在少得多的机器编写的。许多程序没有浮点数学运算,并且它们可能会将默认情况下不常用的每个库都使用了。我猜想UNIX OS的构建时间及其附带的工具是推动力。


我认为问题的根源在于libm的内容很大程度上是标准C库的一部分,为什么它们不在libc中?
Evan Teran

1
gcc的原因是要保持与AT&T Unix中原始cc的兼容性。我在1988年使用3B2,而您必须-lm才能获得数学。当时对我来说,这似乎完全是武断的。在Visual Studio中,我不记得必须添加数学,但是有时您必须添加其他看似c运行时的库。我认为编译器供应商有原因(构建时间?),但是现在,我敢打赌gcc只是试图向后兼容。
Lou Franco

3

如果我输入stdlib.h或stdio.h,则不必链接它们,但在编译时必须链接:

stdlib.hstdio.h是头文件。为了方便起见,将它们包括在内。他们仅预测在链接到正确的库后将提供哪些符号。实现位于库文件中,这是功能的真正所在。

包含 math.h仅仅是获得对所有数学功能的访问的第一步。

同样,libm即使您不使用它的功能,也不必链接,即使您这样做#include <math.h>,对于您的符号编译器来说,这只是一个信息性步骤。

stdlib.hstdio.h指的是中可用的功能libc,这些功能恰好总是被链接在一起,因此用户不必自己做。


2

stdio是标准C库的一部分,默认情况下,gcc将链接到该库。

数学函数的实现位于单独的libm文件中,默认情况下未链接到该文件,因此必须将其指定为-lm。顺便说一下,这些头文件和库文件之间没有关系。


3
他知道那..他在问为什么
埃文·

他说为什么。Simon解释说,默认情况下某些库是链接到的,例如stdio,而默认情况下未链接数学库,因此必须指定它。
mnuzzo

5
我要说的是,问题的本质是在询问为什么默认情况下不链接libm(甚至与libc无关),因为它的内容大部分是c标准库的一部分。
Evan Teran

2

这是一种使根本不使用它的应用程序性能更好的方法。这是我的想法。

x86 OS(以及我想象的其他OS)需要在上下文切换中存储FPU状态。但是,大多数OS仅在应用程序首次尝试使用FPU之后才费心保存/恢复此状态。

除此之外,数学库中可能还包含一些基本代码,这些基本代码会在加载库时将FPU设置为基本状态。

因此,如果您根本不链接任何数学代码,则不会发生任何事情,因此,操作系统根本不必保存/恢复任何FPU状态,从而使上下文切换效率更高。

虽然只是一个猜测。

编辑:作为对某些评论的回应,相同的基本前提仍然适用于非FPU情况(前提是要使不使用libm的应用程序的性能稍好一些)。

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

另外,如果只有静态链接可用,那么将应用类似的论点,即它将减小可执行文件的大小并缩短编译时间。


如果您不链接libm,而是通过其他方式(例如,对浮点数进行操作)触摸x87 FPU,则x86内核确实需要保存FPU状态。我认为这不是一个很好的猜测……
短暂

当然,如果您手动使用FPU,内核仍然需要保存/恢复其状态。我说的是,如果您从不使用它(包括不使用libm),则不必使用它。
Evan Teran 2009年

确实,它可以高度依赖于内核。内核使用的数学库可以具有将其打开的save_FPU_on_switch()函数,而其他函数仅检测是否触摸了FPU。
Earlz

1
如果我没记错的话,整个问题早于浮点协处理器甚至在微处理器上。
Nosredna

@earlz:保存数学库请求的方法将是一个糟糕的设计。如果他们通过其他方式使用FPU怎么办?唯一理智的方法(除了始终保存/还原之外)是检测使用情况,然后开始保存/还原。
Evan Teran
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.