在编译时为什么要加上 –lm ?

Linux 中常用链接来解决一些库函数的问题。在编译时链接可以生成可执行文件。了解一些链接的基本过程,能让我们在开发中减去不少的麻烦!

编译时使用 gcc -lm

程序功能很简单,下面的示例展示了exp()函数的用法。

(代码一)

#include 
#include  //exp


int main(int argc, char const *argv[]){
	
   //double x = 0;
  
   printf("The exponential value of %lf is %lf\n", 0, exp(0));
   printf("The exponential value of %lf is %lf\n", 0+1, exp(0+1));
   printf("The exponential value of %lf is %lf\n", 0+2, exp(0+2));
   
   return(0);
}

编译结果:

在编译时为什么要加上 –lm ?_第1张图片我们再来看下面这种情况:
(代码二)

#include 
#include 

//#define X 0

int main(int argc, char const *argv[]){
	
   double x = 0;
  
   printf("The exponential value of %lf is %lf\n", x, exp(x));
   printf("The exponential value of %lf is %lf\n", x+1, exp(x+1));
   printf("The exponential value of %lf is %lf\n", x+2, exp(x+2));
   
   return(0);
}

编译输出:
在编译时为什么要加上 –lm ?_第2张图片
同样的编译方法却编译不过了,提示对‘exp’未定义的引用。

来看看man手册exp函数的使用:
在编译时为什么要加上 –lm ?_第3张图片
发现exp函数除了需要包含头文件math.h外,编译时还需要使用-lm链接。

编译时在包含链接-lm,看看是否编译通过:

在编译时为什么要加上 –lm ?_第4张图片库链接一般放在命令行结尾。

问题

两段代码同样都调用了exp函数,为什么一个需要链接,一个不需要链接呢?

我们可以观察到,代码一调用exp传入的参数是常量为 0 。代码二调用exp传入的参数是变量 x,那么对于代码一会不会在运行之前就计算好了呢?

来看看它们的汇编代码:
(代码一)
在编译时为什么要加上 –lm ?_第5张图片在编译时为什么要加上 –lm ?_第6张图片
第一段代码没有看到调用exp的身影。实际上,通过汇编代码可以看到,当传入参数为常量时,就已经计算好了值,最后根本不需要调用exp函数。

(代码二)

在编译时为什么要加上 –lm ?_第7张图片在编译时为什么要加上 –lm ?_第8张图片
汇编的具体细节我们无需尽知,变量型的参数,其值在运行时确定,因此需要调用,第二段代码call exp指令调用了exp函数。

ldd命令查看链接库

代码一:

在编译时为什么要加上 –lm ?_第9张图片
在Linux平台上最广泛使用的C函数库是glibc,其中包括C标准库的实现。几乎所有C程序都要调用glibc的库函数,所以glibc是Linux平台C程序运行的基础。glibc提供一组头文件和一组库文件,最基本、最常用的C标准库函数和系统函数在libc.so库文件中,几乎所有C程序的运行都依赖于libc.so (例如 printf)

代码二:

在编译时为什么要加上 –lm ?_第10张图片
使用math.h中声明的库函数还有一点特殊之处,gcc命令行必须加-lm选项,因为数学函数位于libm.so库文件中(这些库文件通常位于/lib目录下),-lm选项告诉编译器,我们程序中用到的数学函数要到这个库文件里找。

总结

调用函数是否需要链接,可以使用命令“man 3 函数名“查看,如果需要链接库,最后都有说明。调用包含于libc库中的函数不需要链接。

在编译时为什么要加上 –lm ?_第11张图片
(微信公众号: 程序猿编码
在编译时为什么要加上 –lm ?_第12张图片
(添加本人微信,备注加群,进入程序猿编码交流群,领取学习资料,获取每日干货)

欢迎关注微信公众号“程序猿编码”,这里Linux c/c++ 、Go语言、数据结构与算法、网络编程相关知识,常用的程序员工具。还有汇聚精炼每日时政、民生、文化、娱乐新闻简报,即刻知晓天下事!

你可能感兴趣的:(C/C++)