如果我在C程序中包含或,则在编译时不必链接它们,但是我必须链接到,例如,将-lm与gcc一起使用:
这是什么原因呢? 为什么我必须显式链接数学库,而不是其他库?
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链接。
-
好的,所以这成为一个库设计问题-为什么以这种方式对库进行分区?
-
我敢打赌,它可以优化UNIX本身(及其附带的工具集)的构建时间。当时,它可能是用C构建的最复杂的东西。
-
与fpu仿真有关系吗?但是对于linux,最终的仿真是在内核中,而不是在libm中。
-
我读过它是因为Unix黑客编写自己的数学函数很普遍。
-
这与Linux无关,因为它早在Linux之前就很普遍。我怀疑这与尝试最小化可执行文件大小有关,因为有很多程序不需要数学函数。
-
在古老的系统上,如果数学函数包含在libc中,则编译所有程序的速度将变慢,输出可执行文件的大小将变大,并且运行时将需要更多的内存,这对大多数根本不使用这些数学函数的程序没有好处。如今,我们已经对共享库提供了很好的支持,即使在静态链接时,也已经设置了标准库,以便可以丢弃未使用的代码,因此这些都不是很好的理由。
-
@ephemient:您是否具有获取有关此信息的链接?
-
@ephemient:最后的评论是一个很好的理由,并且可能是正确的:-P。将其编辑到您的信息中,您便获得了我的+1。
-
@ephemient即使在过去,链接到库也无法将库的所有内容都提取到可执行文件中。链接器尽管经常被忽略,但在历史上一直非常有效。
-
@ephemient此外,共享库的存在时间比您想象的要长。它们是在1950年代而非1980年代发明的。
-
这取决于库归档文件中对象的设置方式,并且由于其中许多系统早于我,因此我无法准确地声明它们。但是,系统创建者确实会做一些额外的工作来拆分库对象以实现此目的,这确实是有道理的。
-
共享库已经存在了很长时间,但是据我所知,即使是libc,许多系统仍在使用静态链接库。例如,Stratus VOS仍在部署和维护,并且大多数部署根本没有共享库...
-
我想,归根结底,我们所看到的不过是海湾合作委员会的保守主义:"它总是那样运作"。我只希望他们对编译器扩展应用相同的推理。
-
我无法现实地想象,如果将libm合并到libc中,任何事情都会破坏(并且libm变成了一个空的存根,因此-lm不会失败)。那好吧。
-
@Niel使用-std = c99或其他方法。
-
为什么在llvm gcc上我可以省略-lm?
-
嗨,我很好奇为什么gcc -std = c89需要-lm标志,clang -std = c89,c99,c11也需要此标志,但是gcc -std = c99,c11不需要。 GCC 4.8.1,clang 3.5 svn中继。
请记住,C是一门古老的语言,FPU是相对较新的现象。我首先在8位处理器上看到C语言,即使要做32位整数算术也需要很多工作。其中许多实现甚至都没有可用的浮点数学库!
即使在最初的68000台计算机(Mac,Atari ST,Amiga)上,浮点协处理器也常常是昂贵的附件。
要进行所有浮点数学运算,您需要一个相当大的库。数学将变得缓慢。因此,您很少使用浮子。您尝试使用整数或可缩放整数进行所有操作。当您必须包括math.h时,您会咬牙切齿。通常,您会编写自己的近似值和查找表来避免这种情况。
权衡存在很长时间。有时会有称为" fastmath"之类的竞争性数学软件包。什么是数学的最佳解决方案?真的很准确但是很慢的东西?不准确但是很快?大表触发功能?直到确保协处理器位于计算机中,大多数实现才变得显而易见。我想象现在有某个程序员在某个嵌入式芯片上工作,试图决定是否引入数学库来处理一些数学问题。
这就是为什么数学不是标准的原因。许多甚至大多数程序都没有使用单个浮点数。如果FPU一直存在,并且浮动和双倍交易总是便宜的话,那么无疑会有" stdmath"。
-
嘿,我在台式机上用Java在(1 + x)^ y中使用Pade近似值。日志,exp和pow仍然很慢。
-
好点子。我已经在音频插件中看到了sin()的近似值。
-
这就解释了为什么默认不链接libm,但是数学是C89的标准功能,在此之前,K&R实际上已经对其进行了标准化,因此您的" stdmath"标记没有意义。
由于荒谬的历史实践,没有人愿意解决。将C和POSIX所需的所有功能整合到一个库文件中,不仅可以避免一遍又一遍地问这个问题,而且在动态链接时还可以节省大量的时间和内存,因为每个链接的.so文件都需要文件系统操作以查找和查找它,还有几页用于其静态变量,重定位等。
所有函数都在一个库中并且-lm,-lpthread,-lrt等选项都是无操作的实现(完全链接到空的.a文件)完全符合POSIX,并且肯定是更可取的。
注意:我说的是POSIX,因为C本身未指定有关如何调用编译器的任何内容。因此,您可以将gcc -std=c99 -lm视为必须调用编译器以实现一致行为的特定于实现的方式。
-
+1指出POSIX不需要存在分离的libm,libc和librt库。例如,在Mac OS上,所有内容都位于单个libSystem(还包括libdbm,libdl,libgcc_s,libinfo,libm,libpoll,libproc和librpcsvc)。
-
–1用于推测库查找对性能的影响,而无需使用链接或数字进行备份。"个人资料。不要猜测"
-
这不是猜测。我没有任何发表的论文,但是我自己完成了所有测量,并且差异很大。只需将strace与一种定时选项结合使用,即可观察到动态链接花费了多少启动时间,或者比较了在所有标准实用程序都是静态链接的系统与动态链接的标准公用程序上运行的.configure。甚至主流的桌面应用程序开发人员和系统集成商都知道动态链接的成本。这就是为什么存在预链接之类的原因。我确定您可以在其中一些论文中找到基准。
-
请注意,POSIX确实要求接受-lm,并且使用数学接口的应用程序必须使用-lm,但是它可以是由编译器命令处理(甚至忽略)的内部选项,而不是实际的库文件。或者,如果接口位于主libc中,则它可能只是一个空的.a文件。
-
@FX:不知道为什么我以前忘了提这个:strace -tt会很容易地向您显示在动态链接上花费的时间。 它不漂亮。 在Linux上,检查procsyssmaps将显示其他库的内存开销。
因为time()和某些其他函数是在C库(libc)本身中定义的,并且GCC始终链接到libc,除非您使用-ffreestanding编译选项。但是,数学函数位于libm中,而gcc没有将其隐式链接。
-
在LLVM gcc上,我不必添加-lm。为什么是这样?
这里给出一个解释:
So if your program is using math functions and including math.h, then you need to explicitly link the math library by passing the -lm flag. The reason for this particular separation is that mathematicians are very picky about the way their math is being computed and they may want to use their own implementation of the math functions instead of the standard implementation. If the math functions were lumped into libc.a it wouldn't be possible to do that.
[编辑]
不过,我不确定我是否同意。如果您有一个提供sqrt()的库,并且在标准库之前传递了它,那么Unix链接器将使用您的版本,对吗?
-
我不认为会发生这种情况。您可能最终会遇到符号冲突。这可能取决于链接程序和库的布局。我仍然发现这个理由很薄弱。如果您要制作自定义的sqrt函数,即使执行相同的操作,也不应给它起与标准sqrt函数相同的名称...
-
确实,创建自己的名为sqrt的函数(非静态)会导致程序具有未定义的行为。
-
@Bastien好找到。接下来,您说"在标准库之前"是什么意思?我认为,标准库默认情况下处于链接状态,不需要通过命令行选项进行链接。因此,标准库将是链接器的第一个入门对象,并且无法在"标准库"之前放置自己的实现。
-
@RockyInde:看我的回答,我想我的意思是"在标准数学库之前"。但是我认为有一些编译器选项可以不链接标准C库,这将允许您传递自己的C库。
-
@BastienLonard我使用的是7.2版的gcc,-lm完全是可选的。有任何想法吗
正如临时人员所说,C库libc默认情况下是链接的,并且该库包含stdlib.h,stdio.h和其他几个标准头文件的实现。只是要添加它,根据" GCC简介",使用C编写的基本" Hello World"程序的链接器命令如下:
1 2 3 4
| 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简介-与外部库的链接中,有关于对外部库的链接的详尽讨论。如果一个库是标准库的成员(如stdio),则无需指定编译器(实际上是链接器)来链接它们。
编辑:在阅读了其他一些答案和评论后,我认为libc.a引用和链接到两者的libm引用中有很多要说为什么两者分开。
Note that many of the functions in 'libm.a' (the math library) are defined in 'math.h' but are not present in libc.a. Some are, which may get confusing, but the rule of thumb is this--the C library contains those functions that ANSI dictates must exist, so that you don't need the -lm if you only use ANSI functions. In contrast, `libm.a' contains more functions and supports additional functionality such as the matherr call-back and compliance to several alternative standards of behavior in case of FP errors. See section libm, for more details.
-
哪个没有回答为什么必须分别在匹配库中链接的问题。显然,您需要单独链接OpenGL库,但是可以说数学库通常很有用。
-
@David:对。从这个问题上我不清楚,这是OP所要问的问题。如您所言,我正在编辑我的答案。
-
我知道我编译使用sqrt函数的程序的原因,该程序无需通过-lm包含库即可工作。谢谢!
If I put stdlib.h or stdio.h, I don't have to link those but I have to link when I compile:
stdlib.h,stdio.h是头文件。为了方便起见,将它们包括在内。他们仅预测在链接到正确的库后将提供哪些符号。实现位于库文件中,这是功能的真正所在。
包括math.h只是访问所有数学函数的第一步。
另外,即使您不使用libm的功能,也不必针对libm进行链接,即使您对符号的编译器执行了#include 只是对您来说是一个参考步骤。
stdlib.h,stdio.h指的是libc中可用的功能,这些功能恰好总是被链接在一起,因此用户不必自己做。
我认为这是任意的。您必须在某处画一条线(哪些是默认库,哪些需要指定)。
它使您有机会用功能相同的另一种替代它,但是我认为这样做不是很普遍。
编辑:(从我自己的评论):我认为gcc这样做是为了保持与原始cc的向后兼容性。我对cc为什么要这样做的猜测是由于构建时间-cc是为功率比现在少得多的机器编写的。许多程序没有浮点数学运算,因此它们可能会将默认情况下不常用的每个库都使用了。我猜想UNIX OS的构建时间及其附带的工具是推动力。
-
我认为问题的根源在于libm的内容很大程度上是标准C库的一部分,为什么不在libc中使用它们呢?
-
gcc的原因是要保持与AT&T Unix中原始cc的兼容性。我在1988年使用3B2,而您必须-lm才能获得数学。当时对我来说,这似乎是完全武断的。在Visual Studio中,我不记得曾经需要添加数学,但是有时您必须添加其他看似c运行时的库。我认为编译器供应商有原因(构建时间?),但是现在,我敢打赌gcc只是试图向后兼容。
我想这是一种使根本不使用它的应用程序性能更好的方法。这是我的想法。
x86 OS(以及我想象的其他OS)需要在上下文切换中存储FPU状态。但是,大多数OS仅在应用程序首次尝试使用FPU之后才费心保存/恢复此状态。
除此之外,数学库中可能还包含一些基本代码,这些基本代码会在加载库时将FPU设置为基本状态。
因此,如果您根本不链接任何数学代码,则不会发生任何事情,因此,操作系统根本不必保存/恢复任何FPU状态,从而使上下文切换效率更高。
只是一个猜测而已。
编辑:作为对某些评论的回应,相同的基本前提仍然适用于非FPU案例(前提是要使不使用libm的应用程序的性能稍好一些)。
例如,如果有一个软FPU,它在C的早期就很不错。那么将libm分开可以防止不必要地链接很多大的(如果使用的话,速度很慢)代码。
另外,如果只有静态链接可用,那么将应用类似的论点,即它将减小可执行文件的大小并缩短编译时间。
-
如果您不链接libm,而是通过其他方式(例如,对浮点数进行操作)触摸x87 FPU,则x86内核确实需要保存FPU状态。我不认为这是一个很好的猜测。
-
当然,如果您手动使用FPU,内核仍将需要保存/恢复其状态。我说的是,如果您从不使用它(包括不使用libm),则不必使用它。
-
确实,它可以高度依赖于内核。内核使用的数学库可以具有将其打开的save_FPU_on_switch()函数,而其他函数仅检测是否触摸了FPU。
-
如果我没记错的话,整个问题很早就出现在浮点协处理器上,甚至是在微处理器上。
-
@earlz:保存数学库请求的方法将是一个糟糕的设计。如果他们通过其他方式使用FPU怎么办?唯一理智的方法(除了总是保存/恢复之外)将是检测使用情况,然后开始保存/恢复。
stdio是标准C库的一部分,默认情况下,gcc将链接到该库。
数学函数的实现位于单独的libm文件中,默认情况下未链接到该文件,因此您必须将其指定为-lm。顺便说一下,这些头文件和库文件之间没有关系。
-
他知道那..他在问为什么
-
那不能解释"为什么"吗?
-
他说为什么。 Simon解释说,默认情况下某些库是链接到的,例如stdio,而默认情况下数学库没有链接到,因此必须指定。
-
我要说的是,问题的性质是在问为什么默认情况下不链接libm(甚至与libc无关),因为它的内容大部分是c标准库的一部分。