一、C 编译优化 - 没有 inline 关键字修饰的函数也可能被内联
1、函数内联的不确定性
现在的 C 编译器能够进行编译优化 ,
- 使用了 inline 声明的 内联函数 , 编译器 可能不会允许该函数 进行内联 ;
- 没有使用 inline 声明的 普通函数 , 如果频繁调用 , 编译器 可能会为了提高执行效率 , 将其内联 ;
内联函数的不确定性 : 编译器内联函数是基于 编译器的优化策略和代码的特性 来决定的 ;
- 不能保证所有函数都会被内联 ;
- 即使函数被内联 , 也不能保证 程序的性能 一定会提高 ;
2、C 编译器的内联优化
简单且频繁调用的函数 内联大概率成功 , 复杂的函数 大概率内联失败 , 内联成功可能会增加代码的大小 , 也可能会导致程序运行速度变慢 ;
可以通过设置调整 C 编译器 的参数 和 优化级别 , 优化编译后的程序运行效果 ;
3、内联优化细节
即使没有使用inline关键字修饰的函数 , C 编译器 根据 函数特性 和 调用频率 , 结合当前的 程序执行效率 和 综合性能 , 决定是否将函数进行内联 ;
内联函数的目的是减少函数调用的开销 , 提高程序的执行效率 ;
编译器在决定是否内联函数时 , 会考虑函数的复杂性 , 大小和调用次数等因素 ;
如果 函数比较简单 且被频繁调用 , 编译器可能会选择将其内联 , 以提高程序的执行效率 ;
二、C 编译器内联限制
1、内联失败的几种情况
内联失败的几种情况 : 如果 内联函数 有如下情况 , 即使使用 inline 关键字声明内联函数 , 也是无效的 ;
- 函数中存在循环 : 内联函数中 不能存在任何形式的 循环语句 , 如 : for / foreach / while / do while 循环 ;
- 函数中有很多条件判定 : 内联函数中 不能存在过多的条件判定语句 , 条件判断就意味着有无效的指令 , 非常浪费空间 ; 一般是不能超过 20 ~ 30 个条件判断语句 , 具体数目可以通过编译器配置 ;
- 函数体庞大 : 函数体的代码不能太多 , 函数指令太多 , 在调用位置插入的指令就很多 , 会浪费很多代码空间 ;
- 对函数进行取地址操作 : 调用函数时 , 尝试获取函数的地址 , 由于 内联函数 是不存在的 , 编译时直接插入到调用位置 , 获取内联函数地址就会导致程序执行失败 , 因此一旦尝试获取内联函数地址 , 内联直接失败 ;
- 内联函数声明在调用之后 : 由于内联函数不能进行声明操作 , 内联函数的声明与定义必须在一起 , 如果内联函数调用在声明定义之前 , 说明该内联函数进行了单独的声明 , 该函数的内联一定会失败 , 作为普通函数处理 ;
2、内联失败的本质分析
函数中 如果 有循环语句 / 有很多条件判定语句 / 函数体庞大 / 对函数取地址操作 / 单独声明内联函数 , 即使写了 inline 内联函数 , 编译器也不会同意内联请求 ;
内联函数 与 普通函数 对比 , 其优势只是 省去了 函数调用时 的 压栈 / 跳转 / 返回 的开销 ;
如果 函数体 由于过大或执行特殊操作 的执行开销 远大于 压栈 / 跳转 / 返回 的开销 , 此时内联函数 反而会降低程序的整体性能 , 编译器会权衡二者对性能的影响 , 同意 / 否决 函数 的 内联请求 ;