✊✊✊每日一面——inline关键字和include头文件
- 一、说说inline的作用及使用方法
- 二、内联函数一定会被编译器在调用点展开吗?
- 三、普通函数一定是被编译器调用吗?
- 四、既然内联函数在编译阶段就已经在调用点被展开,那么在程序运行期间,对应的内存中包不包括内联函数的定义?
- 五、inline函数的工作原理
- 六、宏定义(define)和内联函数(inline)的区别
- 七、为什么不把所以的函数都写成内联函数?
- 八、说说include头文件的顺序以及双引号""和尖括号<>的区别
一、说说inline的作用及使用方法
- inline是一个关键字,可以用于定义内联函数。而内联函数,可以像普通函数那样被调用,但在调用时并不是通过函数的调用机制而是直接在调用点处展开,可以减少由函数调用带来的开销,提高程序运行的效率
- 对于类内定义成员方法,则默认是内联函数不必加关键字inline,因为编译器会自动将类内定义的函数声明为内联函数;
- 对于类外定义成员方法为内联函数,则需要关键字inline声明,这里只需在调用函数之前把inline的信息告知编译器即可
- 补充:
- 消除函数调用的开销:在内联函数出现之前,通常用 #define 定义一些“函数”(这使编译器不会检查其参数的正确性而使用inline,则可以被编译器检查,易于找错)
- 去除只定义一次的限制:内联函数可以在头文件中被定义,并在多个 .cpp文件的include,而不会遇到重定义问题
二、内联函数一定会被编译器在调用点展开吗?
不一定,inline只是对编译器的建议而非命令。编译器也可以选择忽视inline。如果当定义的inline函数包含复杂递归、代码量大,编译器一般不会将其展开,仍然会选择函数调用
三、普通函数一定是被编译器调用吗?
这也是不一定的,即使对于普通函数,编译器也可以对此进行优化,将其在“函数调用点”上展开
四、既然内联函数在编译阶段就已经在调用点被展开,那么在程序运行期间,对应的内存中包不包括内联函数的定义?
肯定包括,如上知,编译器可以选择调用内联函数,而非直接展开它。因此,在内存中仍然需要一份内联函数的定义,以供调用。而且,一致性是所有语言都应该遵守的准则,普通函数有可以有指向它的函数指针,那么内联函数也可以有,因此内存中需要。。。,使得函数指针可以存在
五、inline函数的工作原理
- 内联函数不是在调用时发生控制转移关系,而是在编译阶段将函数体嵌入到每一个调用该函数的语句块中,即编译器将程序中出现的内联函数的调用表达式用函数体来替换
- 普通函数是将程序执行转移到被调用函数所存放的内存地址,当函数执行完后,返回到执行此函数的地方。进行转移操作需要包护现场,被调用函数执行完后,再恢复现场,该过程需要较大的资源开销
六、宏定义(define)和内联函数(inline)的区别
- 内联函数是在编译是展开,而宏是在编译预处理时展开;在编译时,内联函数直接被嵌入到目标代码中去,而宏只是做了简单的文本替换
- 宏定义不是函数,但使用起来像函数,编写较为复杂,常需要一些括号来避免歧义,它通过复制宏代码的方式代替了函数的调用,省去了函数压栈的过程,提高了效率;内联函数本质上是一个函数,它本身不能调用自己
- 宏定义只能进行文本替换,不会检查参数类型、语句是否正常编译等,而内联函数会对参数类型、函数体内部语句是否正确进行检查,内联函数满足函数的性质,比如由返回值、参数列表等
七、为什么不把所以的函数都写成内联函数?
内联函数以代码复杂为代价,它以省去函数调用老提高执行效率。一方面,如果内联函数体代码执行时间比函数调用开销大,则没有太大的意义;另一方面,每一次内联函数的调用都需要复制代码,消耗更多的内存空间,因此以下情况不宜使用内联函数:
- 函数体内的代码比较长,将导致内存消耗代价大
- 函数体内由循环,函数执行时间要比函数调用开销大
八、说说include头文件的顺序以及双引号""和尖括号<>的区别
- 区别:
- 尖括号<>的头文件是系统文件,双引号""的头文件是自定义文件
- 编译器预处理阶段查找头文件的路径不一样
- 查找路径:
- 使用尖括号<>的头文件的查找路径:编译器设置的头文件路径 -> 系统变量
- 使用双引号""的头文件路径:当前头文件目录 -> 编译器设置的头文件路径 -> 系统变量