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

gcc test.c -o test -lm

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


当前回答

我猜,这是一种使根本不使用它的应用程序性能稍好一些的方法。以下是我的看法。

x86操作系统(我想还有其他操作系统)需要在上下文切换中存储FPU状态。然而,大多数操作系统只在应用程序第一次尝试使用FPU时才会保存/恢复这种状态。

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

所以,如果你根本不链接任何数学代码,这一切都不会发生,因此操作系统根本不需要保存/恢复任何FPU状态,使上下文切换稍微更有效。

不过这只是猜测。

同样的基本前提仍然适用于非fpu情况(前提是,它是为了让没有使用libm的应用程序表现得更好)。

例如,如果有一个在c语言早期很可能出现的软fpu,那么将libm分开可以防止大量不必要的大代码(如果使用它的话会很慢)被链接。

此外,如果只有静态链接可用,则适用类似的论点,即它将保持可执行文件的大小和编译时间较短。

其他回答

stdlib.h和stdio.h中的函数在libc中有实现。所以(或libc。A用于静态链接),默认情况下链接到可执行文件中(就像指定了-lc一样)。可以指示GCC使用- nodlib或-nodefaultlibs选项避免这种自动链接。

math.h中的数学函数在libm中有实现。所以(或libm。A表示静态链接),默认情况下不链接libm。libm/libc的分裂是有历史原因的,但没有一个令人信服。

有趣的是,c++运行时libstdc++需要libm,因此如果使用GCC (g++)编译c++程序,将自动链接到libm。

我猜,这是一种使根本不使用它的应用程序性能稍好一些的方法。以下是我的看法。

x86操作系统(我想还有其他操作系统)需要在上下文切换中存储FPU状态。然而,大多数操作系统只在应用程序第一次尝试使用FPU时才会保存/恢复这种状态。

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

所以,如果你根本不链接任何数学代码,这一切都不会发生,因此操作系统根本不需要保存/恢复任何FPU状态,使上下文切换稍微更有效。

不过这只是猜测。

同样的基本前提仍然适用于非fpu情况(前提是,它是为了让没有使用libm的应用程序表现得更好)。

例如,如果有一个在c语言早期很可能出现的软fpu,那么将libm分开可以防止大量不必要的大代码(如果使用它的话会很慢)被链接。

此外,如果只有静态链接可用,则适用类似的论点,即它将保持可执行文件的大小和编译时间较短。

我认为这有点武断。您必须在某个地方划清界限(哪些库是默认的,哪些库需要指定)。

它让你有机会用一个具有相同功能的不同产品来替代它,但我不认为这样做很常见。

我认为GCC这样做是为了保持与原始cc可执行文件的向后兼容性。我猜cc为什么这样做是因为构建时间——cc是为比我们现在的功率小得多的机器编写的。许多程序没有任何浮点数学运算,它们可能会将所有不常用的库从默认值中删除。我猜Unix操作系统的构建时间和与之配套的工具是驱动因素。

注意,即使使用一些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)上进行了测试。

这篇文章给出了一些解释:

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

参见:

GCC提供的其他内置函数 如何让gcc编译器不优化标准库函数调用如printf?

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

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