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

gcc test.c -o test -lm

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


当前回答

注意,即使使用一些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?

其他回答

因为荒谬的历史实践,没有人愿意修正。将C和POSIX所需的所有函数合并到一个库文件中,不仅可以避免反复询问这个问题,还可以在动态链接时节省大量的时间和内存,因为所链接的每个.so文件都需要文件系统操作来定位和查找它,并占用一些页面用于静态变量和重定位等。

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

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

如果我放入stdlib.h或stdio.h,我不需要链接它们,但我必须在编译时链接:

Stdlib.h, stdio.h是头文件。你把它们包括进来是为了方便。如果你链接到适当的库中,它们只预测哪些符号是可用的。实现在库文件中,这是函数真正存在的地方。

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

此外,如果你不使用它的函数,你也不必链接到libm,即使你做了#include <math.h>,这只是一个关于符号的信息步骤,对于编译器来说。

Stdlib.h, stdio.h指的是libc中可用的函数,这些函数总是被链接在一起,这样用户就不必自己做了。

如前所述,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

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

这里给出一个解释:

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

(编辑)

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

因为time()和其他一些函数是内置在C库(libc)中定义的,GCC总是链接到libc,除非你使用- ffrestanding compile选项。然而,数学函数存在于libm中,并不是由gcc隐式链接的。