内联函数是编译器在编译阶段做的优化,它的目的是减少函数的调用时间从而加快函数的运行,代价是代码量会增加,典型的空间换时间的优化。
什么是减少函数调用时间?
我们都知道传统调用函数的时候会把参数压入栈中,然后调用call指令进行跳转。函数执行的时候还要把参数从栈中取出,函数返回的时候还要把返回值放入寄存器,然后调用ret指令返回。如果对于一个复杂的函数这些开销不算什么,但是对于一个简单的函数比如向量的加法,这部分的开销相对来说就不小了。所以我们看DX的数学库全部采用内联函数来编写。内联函数会在编译器把函数的内容展开到调用的地方,简单的说就是把这部分的汇编代码插入到调用的地方,这样就不需要传递参数,进行函数跳转了。看上去和宏的使用方式很类似,但是它们还是有很大区别的,后面再讲。
要想定义一个内联函数只要在函数定义的时候加上inline关键字,比如
inline int add(int a, b)
{
return a + b;
}
在调用内联函数的时候一定要有函数的定义,因为没有定义,编译器就不知道怎么展开函数了。
内联函数是编译器的优化,所以编译器会进行判断这个函数到底需不需内联,也就是说我们使用了inline关键字,编译器也不一定会把它当成内联函数,比如一个函数内有循环,递归,那么编译器就认为函数比较复杂,不需要做内联优化。放一段代码看看编译器是否会自动选择内联。
inline int add1(int a, int b)
{
a++;
b++;
return a + b * b + a * a;
}
inline const int add2(int a, int b)
{
float c = 0;
for (int i = 0; i < 1000000000; i++)
{
float c = a + b;
if (i > 1)
{
c += a;
}
else
{
float d = a + b;
c += d;
}
}
return c;
}
void main()
{
add1(1,1);
add2(2,2);
}
函数add1被当作内联函数处理了,函数add2没有被内联。如何查看是否内联成功了呢?通过反编译我们看一下汇编代码,如果内联成功,函数调用的时候不会有push和call操作,内联失败会进行push和call操作。
add1(1,1);
00451808 mov dword ptr [ebp-14h],1
0045180F mov dword ptr [ebp-8],1
00451816 mov eax,dword ptr [ebp-8]
00451819 add eax,1
0045181C mov dword ptr [ebp-8],eax
0045181F mov ecx,dword ptr [ebp-14h]
00451822 add ecx,1
00451825 mov dword ptr [ebp-14h],ecx
add2(2,2);
00451828 push 2
0045182A push 2
0045182C call add2 (0451055h)
00451831 add esp,8
在工程选项中可以设置设否启用内联优化。如果关闭内联优化,即使符合内联条件,编译器也不会进行内联处理。
内联函数和宏的区别
内联函数是发生在编译器,所以它可以进行函数参数的检查,如果参数有问题,会在编译器报错。
但是宏是发生在预编译阶段,它只是简单的字符串替换,无法进行参数检查。