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

333
如果在C程序中包含了<stdlib.h><stdio.h>,则编译时不需要链接这些库,但是必须使用-lm链接<math.h>,例如使用GCC编译。
gcc test.c -o test -lm

这是为什么?为什么我必须明确链接数学库,但不需要链接其他库?


14个回答

3
我猜这是一种方法,可以使完全不使用它的应用程序表现得稍微更好。以下是我的想法。
x86操作系统(我想其他操作系统也是如此)需要在上下文切换时存储FPU状态。然而,大多数操作系统只在应用程序尝试首次使用FPU时才保存/恢复此状态。
除此之外,当加载数学库时,可能会有一些基本代码将FPU设置为合理的基本状态。
因此,如果您根本不链接任何数学代码,则不会发生任何事情,因此操作系统不必保存/恢复任何FPU状态,从而使上下文切换稍微更有效率。
这只是一个猜测。
对于非FPU情况,相同的基本前提仍然适用(即它是为了使不使用libm的应用程序表现得稍微更好)。
例如,如果存在软FPU,那么早期C语言可能会使用它。然后,单独使用libm可以防止许多大型(如果使用它则很慢)代码不必要地被链接。
此外,如果只有静态链接可用,则类似的论点适用,它将使可执行文件大小和编译时间减少。

如果您不使用libm库,而是通过其他方式触及x87 FPU(例如对浮点数进行操作),那么x86内核确实需要保存FPU状态。我认为这不是一个非常好的猜测... - ephemient
当然,如果您手动使用FPU,则内核仍需要保存/恢复其状态。我的意思是,如果您从未使用它(包括不使用libm),则它就不必这样做。 - Evan Teran
实际上这非常取决于内核。内核使用的数学库可以有一个save_FPU_on_switch()函数来打开它,而其他一些只是检测FPU是否被触摸过。 - Earlz
1
如果我没记错的话,整个问题早在微处理器上出现浮点协处理器之前就存在了。 - Nosredna
@earlz:让数学库请求保存的方法将是一种可怕的设计。如果他们以其他方式使用FPU会怎样?除了始终保存/恢复之外,唯一明智的方法是检测使用情况,然后开始保存/恢复。 - Evan Teran

2

stdio 是标准 C 库的一部分,默认情况下 GCC 会链接它。

数学函数实现在单独的 libm 文件中,不会默认链接,因此您需要指定它 -lm。顺便说一句,这些头文件和库文件之间没有关系。


4
他知道那件事,他在问“为什么”。 - Evan Teran
他问为什么。Simon解释说,有些库是默认链接的,比如stdio,而数学库不是默认链接的,因此必须明确指定。 - mnuzzo
6
我认为这个问题的本质是询问为什么libm默认情况下没有链接(甚至与libc分开),因为它的内容在很大程度上是C标准库的一部分。 - Evan Teran

2
所有类似于stdio.hstdlib.h的库都在libc.solibc.a中实现,并且默认情况下由链接器链接。 libc.so的库会在编译时自动链接并包含在可执行文件中。
但是,math.h的实现在libm.solibm.a中,与libc.so分开。 它不会被默认链接,您必须在GCC中手动链接它,使用-lm标志编译程序。
GNU GCC团队将其设计为与其他头文件分开,其他头文件默认链接,但是math.h文件不会。
在这里阅读项目编号14.3,如果愿意可以阅读全部内容: 为什么需要链接math.h

请看这篇文章:为什么在GCC中要链接math.h?

看一下用法:

使用这个库


这已经在其他答案中说过了。而且这甚至没有回答问题。问题是为什么libm默认没有链接。 - bolov
这意味着math.h库文件是单独编写在libm.so中的,而其他头文件则位于libc.so中。虽然其他头文件会自动链接,但需要手动添加-lm标志来链接math.h。 - Saptarshi das
所有这些都是标准库的一部分。问题是为什么不是默认链接的。“因为gcc团队设计的就是这样”是一个很差的回答。问题是原因是什么。这里有很好的答案详细说明了历史原因。你的编辑使你的答案更好,但我仍然看不出它在所有其他答案中添加了什么价值。 - bolov

2
请注意,即使您使用一些C数学函数,也可能不总需要指定-lm
例如,以下简单程序:
#include <stdio.h>
#include <math.h>

int main() {

    printf("output: %f\n", sqrt(2.0));
    return 0;
}

可以使用以下命令成功编译并运行:
gcc test.c -o test

此代码已在GCC 7.5.0(Ubuntu 16.04)和GCC 4.8.0(CentOS 7)上测试通过。

这里的文章提供了一些解释:

您调用的数学函数是由编译器内置函数实现的

另请参阅:


这应该是最佳答案。 - yuanjianpeng
但是在Manjaro中,GCC 12.2仍然需要链接。 - chenzhongpu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接