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

gcc test.c -o test -lm

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

有帮助吗?

解决方案

stdlib.hstdio.h中的函数在libc.so(或libc.a用于静态链接)中实现,默认情况下链接到您的可执行文件(就像指定了-lc一样)。可以指示GCC避免与-nostdlib-nodefaultlibs选项的自动链接。

math.h中的数学函数在libm.so(或libm.a用于静态链接)中实现,默认情况下libm未链接。这个libc / libstdc++分裂的历史原因,没有一个非常有说服力。

有趣的是,C ++运行时g++需要<=>,因此如果您使用GCC(<=>)编译C ++程序,您将自动获得<=>链接。

其他提示

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

即使在最初的68000台机器上(Mac,Atari ST,Amiga),浮点协处理器通常都是昂贵的附加设备。

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

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

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

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

一个实现,其中所有函数都在一个库中,-lm-lpthread-lrt等选项都是无操作(或链接到空.a文件)完全符合POSIX,当然优选的。

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

因为time()和其他一些函数builtin在C库(libc)本身中定义,而GCC 总是链接到libc ,除非你使用-ffreestanding编译选项。但是,数学函数存在于libm中,而不是由gcc隐式链接。

此处给出了解释:

  

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

[编辑]

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

正如ephemient所说,C库libc默认是链接的,这个库包含stdlib.h,stdio.h和其他几个标准头文件的实现。只需添加它,根据<!> GCC简介 <!> QUOT;基本<!>“Hello World <!>”的链接器命令; C中的程序如下:

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引用都有很多关于为什么这两者是分开的。

  

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

我认为这有点武断。你必须在某处绘制一条线(哪些库是默认的,哪些是需要指定的。)

它让你有机会用具有相同功能的另一个替换它,但我认为这样做并不常见。

编辑:(来自我自己的评论):我认为gcc这样做是为了保持与原始cc的向后兼容性。我猜cc为什么会这样做是因为构建时间 - cc是为功耗远低于我们现在的机器编写的。很多程序都没有浮点数学,他们可能会把每个不常用的库都用掉。我猜测UNIX操作系统的构建时间以及随之而来的工具是驱动力。

  

如果我把stdlib.h或stdio.h放在一起,我不需要链接那些,但我必须在编译时链接:

stdlib.hstdio.h是头文件。为方便起见,请包含它们。他们只预测如果您链接到适当的库中,哪些符号将可用。实现在库文件中,这就是函数真正存在的地方。

包含math.h只是获得所有数学函数访问权限的第一步。

此外,如果您不使用它的函数,则不必链接libm,即使您为编译器执行了有关符号的#include <math.h>(这只是一个信息性步骤)。

libc,<=>参考<=>中可用的功能,这些功能恰好始终链接在一起,这样用户就不必自己动手。

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

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

我会猜测这是一种让不使用它的应用程序表现得更好的方法。以下是我对此的看法。

x86操作系统(我想其他人)需要在上下文切换时存储FPU状态。但是,大多数操作系统仅在应用尝试首次使用FPU后才会保存/恢复此状态。

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

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

只是一个猜测。

编辑:在回复一些评论时,相同的基本前提仍然适用于非FPU案例(前提是制作不使用libm的应用程序会略微执行更好)。

例如,如果在C的早期存在软FPU,那么将libm分开可以防止大量的(并且如果使用的话那么慢)代码被不必要地链接在内。

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top