内联函数/宏定义/const
先搞清楚普通函数的调用过程
函数体被编译成对应的一段汇编代码,在符号表中会生成一个函数名指向这段代码的入口地址。所有调用此函数的地方都会被编译成CALL 函数名指令,然后连接时将函数名替换为函数的入口地址
内联函数
内联函数:即通过inline关键字将一个函数定义为内联函数,当编译器发现某段代码在调用一个内联函数时,其会将该函数的代码整段的插入到当前位置,这样的好处是省去了压栈出栈等函数调用过程,从而加快了程序的运行速度。但是缺点就是每当代码调用该函数,就会在调用处插入该代码段,导致程序的体积增大。而且如果内联函数过于复杂,编译器可能将其视为普通函数
内联函数需要注意:
- 含有递归调用自己的函数不能为inline
- 使用了循环/switch等复杂语句的函数不能为inline
- 由于inline会增加代码的体积,所以最好该函数代码量不超过5行
- 只要在函数的定义前面加了inline,那么就是内联函数,仅仅在声明前加则不是
- 内联函数需要将函数声明和定义都放入头文件中,如果仅仅放入声明,那么编译器找不到函数实现将无法实现替换
C++类中的内联函数:
- 定义在类中成员函数一定是内联函数(即使不手动写inline)
- 显示定义在类中的成员函数一定是内联函数
- 在类中显示声明为inline,同时在类外定义的函数是内联函数
- 在类中没有显示声明为inline,在类外定义成了inline则也是内联函数
- 如果一个成员函数在类中声明不是inlie,则类外定义也不是inline则其不是内联函数
class Test
{
public:
void func1()
{
...
}//内联函数,情况一
inline void func2()
{
...
}//内联函数,情况二
inline void func3();
void func4();
void func5();
}
void Test::func3()
{
...
}//内联函数,情况三
inline void Test::func4()
{
...
}//内联函数,情况四
void Test::func5()
{
...
}//不是内联函数,情况五
宏定义
所谓宏定义即,程序运行用一个标识符来表示另外一个表达式(可以是常量,变量,字符串,函数等),宏定义在程序编译预处理时被替换
宏定义的优点:
- 可以用宏定义来替换程序中经常使用的一些常量,这样当某个常量需要修改时,直接修改宏定义即可,这样很方便,而且也提高了程序的可读性
- 可以用带参的宏定义来完成函数调用的功能,从而减少系统开销(函数调用会存在压栈出栈等保留函数现场和回复函数现场的操作),对于一些简单的函数调用可能就几条语句,那么函数调用所带来的开销将很大,所以用宏定义会直接在预处理阶段在函数调用处展开,从而提高程序执行效率。
宏定义的缺点:
- 宏定义在代码处直接替换展开,可能导致程序较大
- 宏定义无法调试
- 对于带参的宏定义,由于直接替换,不会检查参数的合法性,存在安全隐患
宏定义和全局变量:
- 宏定义在预编译期使用和替换,全局变量则是在运行时,全局变量在编译时初始化
- 宏定义在编译时被替换到引用的位置,而变量则是在运行时分配空间
- 宏定义不可被赋值,其值定义后不可被修改,而变量运行时可以修改
- 宏定义只有在定义所在文件,或者引用所在文件的其他文件中使用(即在头文件中定义的宏可以在包含头文件的源文件中使用,但是源文件中定义的宏,只能该文件使用),而全局变量则是在工程所有文件中使用
宏定义和函数调用:
- 宏定义直接在调用处展开,不占用函数调用的时间,效率更高(但是要占用编译的时间)
- 宏定义只是简单的字符替换,不存在传参和返回值的概念
- 宏定义不存在类型问题,宏名无类型,其参数也无类型,只是一个符号代表,展开时带入指定的字符即可,但是同样也就没有类型检查,可能不安全
- 宏定义在所有函数调用处展开,可能导致源程序变长
- 宏定义不能调试
- 宏定义只是简单的字符替换,所以要多加括号,避免因为优先级而引起的各种问题
宏定义和const:
- 宏定义在预处理阶段展开,const则是在编译运行阶段使用
- 宏定义没有类型,不做类型检查,const是有具体的类型的,在编译阶段会检查
- 宏定义只是进行展开,有多少地方使用,就替换多少次(占用代码段空间),它定义的宏常量在内存中有若干备份;const定义的常量在程序运行过程中只存在一份(占用常量数据区空间),从而const节省了空间,避免不必要的空间浪费
- 宏定义不能调试,而const可以调试
- const不能重复定义某个变量,但是define可以通过undef取消某个符号的定义,然后重新定义
宏定义和内联函数:
- 宏定义由预处理器处理,进行简单的文本替换,而内联函数则是由编译器处理,直接将编译后的函数体插入调用的地方
- 宏定义不可调试,但是内联函数可以调试
- 内联函数的参数有类型检查,而宏定义没有
- 宏定义可以定义常量,但是内联函数只能是函数