内联函数与宏
宏为什么是高效的
在c/c++中,所有以#开头的行都会被预处理器处理,最终生成一个没有任何#符号的代码。
下面主要介绍由define定义的宏,其它有关宏和预处理器的细节参见这里。
- 定义一个常量:
- 定义一个宏可以有象函数一样的参数,参数不进行类型检查,而直接进行替换。格式很重要,否则会出现意想不到的错误:
#define F (x) (x+1) int main(){ F(1); //预处理器展开以后:(x) (x+1)(1) return 0; }
- 宏的参数在宏展开前不进行计算,可能会出现意外的错误:
#include <stdio.h> #define MULTIPLY(a, b) a*b int main() { // The macro is expended as 2 + 3 * 3 + 5, not as 5*8 printf("%d", MULTIPLY(2+3, 3+5)); return 0; } // Output: 16
以及这个例子:#define square(x) x*x int main() { int x = 36/square(6); // Expended as 36/6*6 printf("%d", x); return 0; } // Output: 36
宏的效率之所以高是因为预处理器直接用宏代码替换宏调用,所以就没有了参数压栈、生成汇编语言的CALL、返回参数、执行汇编语言的RETURN等的开销。
在c++中,使用预处理器宏存在两个问题:第一个问题在C中也存在:宏看起来像一个函数调用,但并不总是这样,这样就隐藏了难以发现的错误,如以上所举的例子;第二个问题是c++特有的:预处理器不能访问类的成员数据,这意味着预处理器宏不能用做类的成员函数。
为了既保证预处理器的效率,而又增加安全性 ,而且 还能像一般成员函数一样可以访问类的数据成员,c++引入内联函数(inline function)。而从c99开始,c语言也引入了内联函数。
内联函数(inline function)
内联函数是真正的函数,它能够像普通函数一样具有我们所期望的任何行为,唯一不同之处是内联函数在适当的地方像宏一样展开,而不需要函数调用的开销,因此,应该避免使用宏,转而使用内联函数。
如何得到内联函数
1、对于在类的内部定义的成员函数都自动变为内联函数;当成员函数在类外定义时,只要前面加上inline关键字,而类内部的函数声明不加inline,也可以成为内联函数。
2、对于一般函数,需要在函数原型和函数定义处都加上inline关键字才能把这个函数变成内联函数。
当然以上两个规定对编译器来说也不是“圣旨”,编译器会根据函数的实际情况来决定是否将函数看作内联函数,因为①当编译器看到带inline关键字的函数时,它会把函数原型和函数体放到符号表里,当使用函数时,编译器检查以确保调用正确且返回值被正确使用,然后将函数调用替换为函数体。如果函数体比较大或者包含任何形式的循环,导致内联代码占用的空间比它作为一个普通函数调用而产生的代码占用的空间还大的话,编译器会把它当作普通函数看待;②假如要显式或者隐式的取函数地址,编译器也不能执行内联,因为这时编译器必须为函数代码分配内存从而产生一个函数的地址,但当地址I不需要时,编译器仍可内联代码
内联函数的定义一般放在头文件里。