一个有趣却容易忽略的编译链接问题

2023-05-14 59浏览

你知道,为什么在C中使用了pow函数不需要链接libm库吗?

什么情况需要指定链接库?

在解释之前,先回顾一下。

//来源:公众号【编程珠玑】

//https://www.yanbinghu.com

#

include

#

include

int

 

main

(

void

)

{

    

int

 b = 

2

;

    

double

 a = 

pow

(b,b);

    

printf

(

"%f\n"

,a);

    

return

 

0

;

}

代码本身比较简单,pow(x,y)用于计算x的y次幂。这里由于pow函数并不在lib库中,默认情况下,

编译

的时候只链接了libc库,因此如果不指定链接库,则会报错:

$ gcc -o test test.c

/tmp/ccKDgtzz.o: 

In

 

function

 `main

':

test.c:(.

text

0x22

): undefined reference 

to

 `pow

'

collect2: 

error

: ld returned 

1

 

exit

 status

从报错信息来看,我们知道是找不到pow函数的定义。通过man手册也可以查看到,编译是需要包含头文件math.h和链接数学库的:

$ man 

pow

       

#

include

 

       

double

 

pow

(

double

 x, 

double

 y)

;

       

float

 

powf

(

float

 x, 

float

 y)

;

       

long

 

double

 

powl

(

long

 

double

 x, 

long

 

double

 y)

;

       Link with -lm.

即像下面这样是可以的:

$

 gcc -o 

test

 test.c -lm

$

 ./

test

4.000000

gcc在编译的时候默认链接的是libc库,其他库需要手动指定。但是,看下面的代码:

//来源:公众号【编程珠玑】

//作者:守望先生

#

include

#

include

int

 

main

()

{

    

int

 b = 

2

;

    

double

 a = 

pow

(b,b);

    

std

::

cout

::

endl

;

    

return

 

0

;

}

编译运行:

$

 g -o 

test

 test.cpp

$

 ./

test

4

我们并没有手动指定链接库,但是却可以编译过!

那么,问题来了!

为何在C中使用pow函数不需要手动指定链接数学库libm,而C里面却需要呢?为了解答这个问题,我们必须知道C的程序中,到底有没有链接libm库。如何查看呢?还记得在《linux常用命令-开发调试篇》中提到的ldd吗?它可以查看当前程序链接了哪些动态库:

$

 ldd 

test

    linux-vdso.so.1 =>  (0x00007ffd41bda000)

    libstdc.so.6 => /usr/lib/x86_64-linux-gnu/libstdc.so.6 (0x00007f6230cca000)

    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f62309c1000)

    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f62305f7000)

    /lib64/ld-linux-x86-64.so.2 (0x00007f623104c000)

    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f62303e1000)

看到没有,除了链接了*基本的libstdc库,还链接了libc和libm库!也就是说,虽然没有手动指定链接,但是实际上还是链接了。但是这是什么时候链接上的呢?我们再看libstdc的链接库发现:

$ ldd /usr/lib32/libstdc.so

.6

    linux-gate.so

.1

 =>  (

0xf7f92000

)

    libm.so

.6

 => /lib/i386-linux-gnu/libm.so

.6

 (

0xf7da6000

)

    libc.so

.6

 => /lib/i386-linux-gnu/libc.so

.6

 (

0xf7bf0000

)

    /lib/ld-linux.so

.2

 (

0xf7f94000

)

    libgcc_s.so

.1

 => /lib/i386-linux-gnu/libgcc_s.so

.1

 (

0xf7bd3000

)

看见没有!原来是libstdc已经链接了libm库,这也就解释了为什么C代码中不需要手动指定链接数学库libm。

总结:

通过前面的内容我们总结如下:

C代码编译时,通常会默认链接libc库

C代码

编译

时,通常会默认链接libstdc库

由于libstdc中有依赖一些数学函数或者libc库中的函数,因此默认链接了libc库和libm库。

作者:守望先生

来源:编程珠玑

版权归原作者所有,如有侵权,请联系删除。

免责声明

以上信息仅作为转载信息展示,不代表作者任何观点.转载作品均注明出处,本网未注明出处和转载的,是出于传递更多信息之目的,并不意味着赞同其观点或证实其内容的真实性。如转载作品侵犯作者署名权,或有其他诸如版权、肖像权、知识产权等方面的伤害,并非本网故意为之,在接到相关权利人通知后将立即加以更正。