让我们来看看几个函数。这是第一个:
#include <math.h>
double func(double num) {
return sqrt(num);
}
用gcc -O3 -c so.c编译,用objdump -d so.o反汇编:
0000000000000000 <func>:
0: f2 0f 51 c8 sqrtsd %xmm0,%xmm1
4: 66 0f 2e c9 ucomisd %xmm1,%xmm1
8: 7a 05 jp f <func+0xf>
a: 66 0f 28 c1 movapd %xmm1,%xmm0
e: c3 retq
f: 48 83 ec 08 sub $0x8,%rsp
13: e8 00 00 00 00 callq 18 <func+0x18>
18: 48 83 c4 08 add $0x8,%rsp
1c: c3 retq
所以我们有一堆 FPU 寄存器操作和一个调用。让我们尝试看看该调用调用了什么。运行nm so.o:
0000000000000000 T func
U sqrt
所以我们定义(T)一个名为“func”的函数,并导入(U)一个名为sqrt的符号。最终的程序必须与-lm 链接,因为这是sqrt 实际定义的地方。
现在让我们尝试第二个功能:
#include <math.h>
double func() {
return sqrt(4);
}
再次,使用gcc -O3 -c so.c 编译并使用objdump -d so.o 反汇编:
0000000000000000 <func>:
0: f2 0f 10 05 00 00 00 movsd 0x0(%rip),%xmm0 # 8 <func+0x8>
7: 00
8: c3 retq
没有 FPU 寄存器操作。没有函数调用。让我们通过nm so.o 进行验证:
0000000000000000 T func
0000000000000000 r .LC0
是的。不导入符号。
如果您省略#include <math.h>,您会得到这里发生了什么的提示:
$ gcc -O3 -g -c so.c
so.c: In function ‘func’:
so.c:2:10: warning: incompatible implicit declaration of built-in function ‘sqrt’
return sqrt(4);
^
sqrt 是一个内置函数。编译器不会将其视为某种不透明的构造。它了解它的作用。在第二种情况下,编译器明白它不需要在运行时调用函数。它在编译期间知道答案是什么。它只是将答案放在返回寄存器中并称之为一天。由于运行时不包含对sqrt 的调用,因此无需与-lm 链接。