大家好,距离上一次更新已经过去一个多月了。前段时间是秋招时期,所以本人也是在忙着找工作、笔试、面试什么的,所幸有之前学习的内容做支撑,还算比较顺利找到了一份软件开发的工作。在多次的面试和笔试当中也收获了许多的经验,日后如果有时间,我也将分享一些有意思的东西。
好了,废话少说,回归到今天的主题。今天想要分享的是内联函数和宏定义。在我的某次笔试中也出现过一次。题目大意问的是在C语言和C++中分别用什么来处理一段短小、反复被调用的代码。我虽然回答出来了,但其实我当时并不是特别了解内联函数,只是听过而已。今天就来稍微理一理。
宏定义相信大家都很清楚了,即使是初学者,也知道宏定义有个好处是可以批量的替换一些变量啊或者一小段代码,提高程序的移植性。但是内联函数可能不是大家都知道。
内联函数是在函数前加上inline关键字,这样的函数就被声明为内联函数,inline是C++的关键字,C语言本身是不支持内联函数的,但是后来在C99标准中支持了内联函数,当然,具体在C语言中能不能用和编译器也有关系。
为什么要引入内联函数呢?主要是为了消除函数调用时的系统开销,以提高运行速度。我们知道,在调用函数的时候,系统要将程序中的一些状态信息存到栈中,然后再跳转执行,在参数保存和传递的过程中是需要时间和空间的开销,使得效率下降,特别是在频繁地调用函数的时候。
而内敛函数不一样,它是进行替换,也就是说在调用函数的时候,它不需要跳转去执行,因为在编译阶段就把函数体“搬过来”了,这是一种用空间换时间的措施。来看个例子:
#include<iostream>
using namespace std;
#define sums(a,b) a+b
inline int fun(int a,int b)
{
return a+b;
}
int main(void)
{
int a=1,b=2;
cout<<"inline:a+b="<<fun(a,b)<<endl;
cout<<"sums:a+b="<<sums(a,b)<<endl;
}
在这里,分别用宏和内敛函数来实现两数之和,效果上是一样的(注意如果inline函数定义在被调用的函数之后,需要在前面声明)。这里使用的是C++,本来想用C的,但是在我的vscode上好像不支持inline,干脆就用C++了。
既然宏定义和内敛函数都可以完成替换,为什么还要引入内联函数呢?因为使用宏定义有时会产生意想不到的错误,这也是笔试题中经常考的地方。
什么错误呢?来看下面的例子,只是在上面的例子上多乘一个2.
int main(void)
{
int a=1,b=2;
cout<<"inline:a+b="<<2*fun(a,b)<<endl;
cout<<"sums:a+b="<<2*sums(a,b)<<endl;
}
我们的本意是希望先计算a+b的值,然后将值*2,于是得结果6.但是一运行,会发现不是这样
在inline里面,正确计算出了结果6,而宏里面却得到4.因为宏展开后是这样的:2*a+b
这样就出现了错误。这是很多初学者容易犯的错误,也是在面试题中的高频考点,至少在我最近的面试题中频繁出现了。所以,使用宏定义要格外小心,通常都会要求我们在后面加上括号,这样就不会出错了。
#define sums(a,b) (a+b)
但是总的来说,内联函数具有带参宏定义的优点而不会出现其副作用,所以inline函数会更安全一些。
那么,使用inline函数需要注意什么呢?
1、inline函数在第一次被调用前必须进行完整的定义,否则编译器无法知道应该插入什么代码。
2、在inline函数里一般不能含有复杂的控制语句,如for、switch等
3、inline函数是一种用空间换时间的措施,函数体不宜太长,否则反而会增大系统开销,一般为1~5条语句。
4、inline和宏定义相似,但不完全相同,宏定义只做简单的字符替换而不做语法检查,往往会出现意想不到的错误。