如果我在C程序中包含<stdlib.h>或<stdio.h>,我在编译时不需要链接这些,但我必须链接到<math.h>,使用-lm与GCC,例如:
gcc test.c -o test -lm
这是什么原因呢?为什么我必须显式地链接数学库,而不是其他库?
如果我在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数学函数,-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视为必须调用编译器以实现一致行为的特定实现方式。
这里给出一个解释:
因此,如果您的程序使用数学函数并包含math.h,那么您需要通过传递-lm标志显式地链接数学库。这种特殊分离的原因是数学家对计算数学的方式非常挑剔,他们可能希望使用自己的数学函数实现而不是标准实现。如果数学函数集中到libc中。A这是不可能的。
(编辑)
不过,我不确定我是否同意这一点。如果你有一个库,它提供了,比如说,sqrt(),并且你在标准库之前传递它,Unix链接器会获取你的版本,对吗?
我认为这有点武断。您必须在某个地方划清界限(哪些库是默认的,哪些库需要指定)。
它让你有机会用一个具有相同功能的不同产品来替代它,但我不认为这样做很常见。
我认为GCC这样做是为了保持与原始cc可执行文件的向后兼容性。我猜cc为什么这样做是因为构建时间——cc是为比我们现在的功率小得多的机器编写的。许多程序没有任何浮点数学运算,它们可能会将所有不常用的库从默认值中删除。我猜Unix操作系统的构建时间和与之配套的工具是驱动因素。
这是个bug。你不必再显式地指定-lm了。也许如果有足够多的人抱怨,它就会被修复。(我并不真的相信这一点,因为坚持这种区别的维护者显然非常固执,但我希望如此。)