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

Nop*_*ope 241 c compilation math.h

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

gcc test.c -o test -lm
Run Code Online (Sandbox Code Playgroud)

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

eph*_*ent 232

在功能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链接到.

  • 在古代系统中,如果数学函数包含在libc中,那么编译所有程序会更慢,输出可执行文件会更大,运行时需要更多内存,对于不使用这些数学函数的*大多数*程序没有任何好处所有.这些天我们对共享库有很好的支持,即使在静态链接时,也会设置标准库,以便可以丢弃未使用的代码,因此这些都不是很好的理由. (36认同)
  • @ephemient即使在过去,链接到库也没有将库的所有内容都拉到可执行文件中.尽管经常被忽视的技术,连接器在历史上一直非常有效. (35认同)
  • 这与Linux无关,因为它早在Linux之前很常见.我怀疑它与尝试最小化可执行文件大小有关,因为有很多程序不需要数学函数. (8认同)
  • @ephemient此外,共享库的存在时间比您想象的要长.它们是在20世纪50年代发明的,而不是20世纪80年代. (7认同)
  • 我想在一天结束时我们所看到的只不过是GCC的保守主义:"它总是像那样".我只希望他们将相同的推理应用于他们的编译器扩展. (5认同)
  • 我无法现实地想象如果 libm 合并到 libc 中,任何东西都会被破坏(并且 libm 变成一个空存根,所以 `-lm` 不会失败)。那好吧。 (2认同)

Nos*_*dna 74

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

即使在最初的68000台机器(Mac,Atari ST,Amiga)上,浮点协处理器通常也是昂贵的附加装置.

要做所有的浮点数学,你需要一个相当大的库.数学运算会很慢.所以你很少使用花车.您尝试使用整数或缩放整数执行所有操作.当你必须包括math.h时,你咬紧牙关.通常,您会编写自己的近似值和查找表以避免它.

权衡取舍存在很长时间.有时会有竞争性的数学包称为"fastmath"等.什么是数学的最佳解决方案?真的准确但缓慢的东西?不准确但速度快?trig功能的大表?直到协处理器被保证在计算机中,大多数实现变得明显.我想现在有一些程序员在那里,在嵌入式芯片上工作,试图决定是否引入数学库来处理一些数学问题.

这就是数学不标准的原因.许多或大多数程序都没有使用单个浮点数.如果FPU一直存在并且浮动和双打总是便宜的操作,毫无疑问会有一个"stdmath".

  • 这解释了为什么`libm`在默认情况下没有被链接,但数学是来自C89的*标准*,在此之前,K&R实际上*标准化它,所以你的"stdmath"评论没有意义. (11认同)

R..*_*R.. 72

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

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

注意:我在谈论POSIX,因为C本身没有指定有关如何调用编译器的任何内容.因此,您可以将其gcc -std=c99 -lm视为特定于实现的方式,必须为符合行为调用编译器.

  • 这不是猜测.我没有任何已发表的论文,但我自己完成了所有的测量,差异很大.只需使用`strace`和其中一个时序选项来观察在动态链接上花费了多少启动时间,或者比较在所有标准实用程序是静态链接的系统上运行`./configure`而不是在它们是动态链接的系统上运行`./configure` - 连接.即使是主流桌面应用程序开发人员和系统集成商也意识到动态链接的成本; 这就是prelink之类的东西存在的原因.我相信你可以在其中一些论文中找到基准. (11认同)
  • +1表示POSIX不要求存在分离的libm,libc和librt库.例如,在Mac OS上,所有内容都位于单个libSystem中(其中还包括libdbm,libdl,libgcc_s,libinfo,libm,libpoll,libproc和librpcsvc). (9认同)
  • @FX:不知道为什么我之前忘记提到这个:`strace -tt`会很容易地显示你在动态链接上花的时间.它不漂亮.在Linux上,检查`/ proc/sys/smaps`将显示其他库的内存开销. (6认同)
  • -1用于推测库查找对性能的影响,而无需使用链接或数字进行备份."简介.不要推测" (3认同)
  • @TimBird:大部分答案似乎错误地假设链接库会从中提取所有内容,而不仅仅是您使用的函数(以翻译单元粒度,但历史上它们被正确地拆分为单独的函数)。 (3认同)
  • 请注意,POSIX *确实* 需要接受`-lm`,并且使用数学接口的应用程序必须使用`-lm`,但它可以是编译器命令处理(甚至忽略)的内部选项,而不是实际的库文件。或者,如果接口在主 libc 中,它可以只是一个空的 `.a` 文件。 (2认同)

ism*_*ail 33

因为C库()本身定义了time()一些其他函数,并且GCC 总是链接到libc,除非你使用compile选项.但是,数学函数存在于gcc中并未隐式链接.builtinlibc-ffreestandinglibm

  • 在LLVM gcc上我不必添加-lm.为什么是这样? (8认同)

Bas*_*ard 26

这里给出一个解释:

因此,如果您的程序使用数学函数和包含math.h,那么您需要通过传递-lm标志显式链接数学库.这种特殊分离的原因是数学家对计算数学的方式非常挑剔,他们可能想要使用他们自己的数学函数实现而不是标准实现.如果将数学函数集中到libc.a其中就不可能这样做.

[编辑]

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

  • 我认为没有保证会发生这种情况; 你最终可能会遇到符号冲突.它可能取决于链接器和库的布局.我仍然觉得自己很虚弱; 如果你正在制作一个自定义sqrt函数,你真的不应该给它与标准sqrt函数同名,即使它做同样的事情...... (10认同)
  • 实际上,使您自己的函数(非静态)命名为 `sqrt` 会导致程序具有未定义的行为。 (2认同)

Bil*_*ard 6

An Introduction to GCC - Linking with external libraries 中对链接到外部库进行了深入讨论。如果库是标准库的成员(如 stdio),那么您不需要指定编译器(实际上是链接器)来链接它们。

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

请注意,“libm.a”(数学库)中的许多函数在“math.h”中定义,但在 libc.a 中不存在。有些是,这可能会令人困惑,但经验法则是这样的——C 库包含 ANSI 规定必须存在的那些函数,因此如果您只使用 ANSI 函数,则不需要 -lm。相比之下,`libm.a' 包含更多功能并支持附加功能,例如 matherr 回调和在 FP 错误的情况下遵守几种替代行为标准。有关更多详细信息,请参阅 libm 部分。


ard*_*srk 5

正如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
Run Code Online (Sandbox Code Playgroud)

注意链接C库的第三行中的选项-lc.