内联函数
在学习函数栈帧时我们知道,只要我们去调用函数,就会在栈上开辟一个空间用来。每次去调用的时候会有一些时间开销。但是在学习C语言的时候我们学习过一个叫做宏的东西,比如下面的代码。
#define ADD(A,B) ((A) + (B))
int add(int x, int y){
return x+y;
}
int main(){
int a = ADD(1, 2); // 调用宏
int b = add(1, 2); // 调用函数
return 0;
}
这两种方式都可以实现两个数求和的功能,那么这两种方式有什么区别呢?在调用函数实现两个数相加时,会在栈上开辟一个空间用来存放函数,调用多少次就开辟多少个空间,在函数使用完后就会释放掉栈上的空间。如果使用宏,会在程序的编译阶段就先将这个表达替换掉,就不存在在栈上开辟空间的情况。
#define ADD(A, B) ((A)+(B))
int main(){
int a = ADD(1, 2); // 在程序编译的时候这条语句会被替换掉,即
// int a = ((1) + (2));
return 0;
}
用宏这种方式虽然可以省去程序在栈上开辟空间的这一步,但是,写宏是一件很蛋疼的是,因为宏和函数不一样,它完全替换的,所以写起来很麻烦。所以,C中就引入了一个新的玩意,叫内联函数。
概念
以inline修饰的函数就叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。
这是一个一般的函数调用,我们可以发现,程序在编译的时候是对其进行跳转,也就是说,函数在被调用的时候是在栈上开辟了空间的。那么下面我们看看内联函数:
特性
inline
是一种以空间换时间的做法,省去调用函数额外开销。所以代码很长或者有循环或递归的函数都不适宜使用作为内联函数inline
对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline
的函数体内有循环/递归等,编译器优化时会忽略掉内联。inline
不建议声明和定义分离,分离会导致链接错误。因为inline
被展开,就没有函数地址了,链接就会找不到。
extern “c”
有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern"C",意思是告诉编译器,将该函数按照C语言规则来编译。比如:tcmalloc是google用C++实现的一个项目,它提供tcmalloc()和tcfree两个接口来使用,但如果是C项目就没办法使用,因为我们知道C语言和C++在函数的处理上有些不同的地方,C++有命名修饰,那么就可以使用exturn"C"
来解决这一问题。语法如下
// 在C++中
exturn "c" int Add(int left, int right);
这样在C中语言环境实现的函数,C++环境下也可以正常使用。关键字exturn"C"
,就是告诉C++编译器函数Add
是按照C语言的命名规则来编译链接的,此时C++编译器就会按照c语言的函数名去函数列表找这个函数。
那么,怎么让c语言使用C++编译生成的函数呢?答案同样是使用extern"C"
来实现,不过这次会稍微困难一点。
// 头文件
#ifdef __cplusplus // __cplusplus是C++编译器自己定义的
extern "C"
{
#endif
void StackInit(ST* ps);
void StackDestroy(ST* ps);
void StackPush(ST* ps);
void StackPop(ST* ps);
#ifdef __cplusplus
}
#endif
这几行代码的意思是C++编译器将按照c语言的方式来处理函数。这样在C语言中使用这个库的时候,也能正确链接到函数。
—end