内联函数与宏定义函数
面试时被问到了这个问题,当时突然懵了,所以来整理一下。如有疏漏,还望指摘。
内联函数与宏定义的函数很相似,都是在运行前,先将一些简短的函数,直接替换为实现代码,以此来省去函数调用时,参数入栈、程序跳转等的时间开支。
宏定义的函数:
使用宏定义#define定义的函数。其在预编译阶段就被进行简单的替换,因为只是简单的替换,所以无需定义参数和返回值的类型,也不会对参数进行临时拷贝以及析构。如下宏定义函数:
#define ADD(a, b) a+b
void main(){
int x=1, y=2, z;
z = ADD(x,y);
}
这段代码就会被替换为如下代码:
void main(){
int x=1, y=2, z;
z = x+y;
}
宏定义函数使用时值得注意以下三点:
-
首先,宏定义函数不会进行类型检查,它不会检查传入的参数类型是否正确,传出的参数类型是否正确,因为在定义时,就不需要规定这些。
-
其次,因为只是进行简单的替换,所以如果不注意的话,执行顺序可能会和预想的不一样。比如还是上面的宏定义函数,如果我想计算
z=3 * ADD(x,y);
就会被替换为
z = 3 * x + y;
这样在执行时,其实会先计算3*x,在计算+y,而与我们预想的逻辑不同。
-
最后,因为不会制作传入参数的临时拷贝,而只是进行简单的替换,所以如果传入的参数是一个表达式(如x++),或者是一个函数(func())的话,这个表达式或者函数可能会被多次执行。如下:
#define ADD(a) a+a void main(){ int x=1, y; y = ADD2(x++); }
这段代码在执行时就会被替换为:
void main(){ int x=1, y; y = x++ + x++; }
其中x++就会被多次执行。
内联函数:
使用关键词inline修饰的函数为内联函数。内联函数是指在编译阶段,编译器将内联函数展开,替换为等效的实现代码,而不是进行函数调用。内联函数在使用上,在程序员看来,和一般函数无二,需要定义入参类型,返回值类型等,执行完毕会对函数内部的临时变量进行析构等。如下内联函数:
inline int addint(int a, int b){
return a+b;
}
void main(){
int x = 1, y = 2, z;
z = addint(x ,y);
}
这段代码,在编译时就会被替换下面这样:
void main(){
int x = 1, y = 2, z;
{
int _a = x; //制作传入参数的临时拷贝
int _b = y;
int temp; //制作传出参数的临时拷贝
temp = _a + _b; //函数体
z = temp; //传出返回值
}//代码块结束,析构一些临时变量
}
编译器将函数替换为实现代码,并加入一些处理,使得其执行起来在程序员看来,与一般函数无二。
内联函数在使用时需要注意几点:
- 首先,类内定义的函数,会被默认为内联函数。
- 其次,即使定义了内联函数,编译器也会进行检查,如果函数太复杂,也不会进行替换。
- 再有,内联函数关键字和函数声明写在一起是无效的,必须与函数的定义写在一起才能生效。
- 最后,如果内联函数需要在其他源文件中进行调用,那么内联函数的定义必须写在头文件中,写在源文件中会造成编译出错。
- 最后的最后,在多个源文件中可以定义相同的内联函数,但是在一个源文件中,只能有一个定义相同的内联函数。
从上面的介绍,我们可以看出以下几个相同点和不同点。
相同点:
- 都是代码的替换。
- 代码的逻辑都应该是简短的。
不同点:
- 内联函数的替换是在编译阶段,宏定义的替换是在预编译阶段。
- 内联函数会进行类型检查,宏定义函数不会进行类型检查。
- 内联函数的参数中,表达式和函数只会被执行一次,宏定义函数中可能会被执行多次。
- 内联函数的执行顺序和正常函数一样,宏定义函数替换后,表达式的执行顺序可能会有不同。
上一篇: Java面试基础宝典