C++——inline function
前言
当代码写复杂后,一定会封装出大量的函数,这会导致两个问题:
①函数越多,栈的消耗也越厉害
疑问:为什么代码复杂了、函数变多了,栈消耗的就很厉害?
答:因为这会导致函数的调用深度可能会很深,比如:
fun1 --> fun2 --> fun3 --> fun4 --> fun5 ---> ...
在这些函数都没有返回之前,所有函数所消耗的栈空间,将一直不会被释放。如果复杂程序还有大量使用线程的话,线程函数还会占用栈空间。
②函数的调用过程,会多花费更多额外的时间
调用函数时,除了函数代码本身执行的时间外,还需要花费额外的时间,比如:
1)从调用位置跳转到函数代码处
2)从栈中开辟空间,将形参、自动局部变量、返回地址压栈
3)利用返回地址返回,以及返回时的弹栈
这些都是需要额外时间的,特别是如果这个函数的调用非常频繁的话,累积花费的就更多。
解决之道
对于那些被频繁调用,而且“代码很简短”的函数来说,我们往往就使用“带参宏”和“内联函数”代替,以减少这类函数的数量,如此一来:
1)函数减少了,在一定程度上节约了栈内存
2)消除了函数调用所需的额外时间,效率更高
C/C++都支持带参宏、内联函数
带参宏
参考
宏——基础
宏——高级
代码演示
#include <stdio.h> int my_max(int a, int b)
{
a *= ;
b /= ;
return (a>b) ? a : b; //找出最大值
} int main(void)
{
int ret = ;
ret = my_max(, );
printf("ret = %d\n", ret);
return ;
}
如果my_max调用非常频繁的话,做成函数形式,其实是非常不划算的,所以完全可以使用带参宏来代替。
#include <stdio.h>
#define MY_MAX(a, b, ret) \
ret = ((a*)>(b/)) ? (a*) :(b/) int main(void)
{
int ret = ;
MY_MAX(, , ret);
printf("ret = %d\n", ret);
return ;
}
进行宏替换后,main函数就变为了
int main(void)
{
int ret = ; ret = ((*)>(/)) ? (*) :(/); //找出最大值
printf("ret = %d\n", ret); return ;
}
使用宏来代替后,“宏体”就变成了“引用者”的一部分,如此一来不仅节约了栈空间,也免去了函数调用时的额外开销。
使用带参宏代替函数时的要求
①只用于对3~5行代码的简短函数进行替换,为什么只替换3~5行代码的简短函数呢?
使用宏来代替函数,缺点也是很明显的,因为宏替换后,使用宏的函数的代码会增加,如果很多函数都有使用这个宏的话,程序的代码量就会急剧增加,毕竟代码也是需要存储空间的。为了节省点栈空间、以及降低“函数调用”所消耗的额外时间,结果导致整个程序代码的剧增,这就得不偿失了。
②而且只替换被频繁调用的函数
替换这类函数才有明显的效果,否则占那么点小便宜也没有什么意思。
带参宏的缺点
带参宏的参数只用于替换,不涉及参数类型的检查,所以如果我们把参数写错了,在预编译进行宏替换时,是不会提示错误或者警告的,这不利于代码排错。
#define MY_MAX(a, b, ret) \
ret = ((a*)>(b/)) ? (a*) :(b/); int main(void)
{
int ret = ;
MY_MAX(dsf344, , ret);
printf("ret = %d\n", ret);
return ;
}
参数指定为dsf344肯定是错的,但是宏在替换时,只进行替换,不会进行参数检查,但是这个你要是搁在函数里面,函数会进行严格的参数检查,如果不对就会报错。为了改进带参宏不会进行参数检查的缺点,后来从c99标准开始就有了"内联函数"。
内联函数
内联函数是个啥
内联函数既不是函数也不是宏,它一个兼具宏和函数共同特点的这么个特殊的玩意。
①具有宏的特点,会像宏一样进行替换
不过宏是在“预处理阶段”进行替换的,而内联函数则是在“编译、链接”阶段进行替换的。替换的过程也被称为“内联”的过程,所以才被称为“内联函数”。
②也具有函数的特点,会像函数一样进行参数类型检查
一句话来概括的话,内联函数就是会像函数一样进行“参数类型检查”的带参宏。
内联函数举例
inline关键字
这个是内联函数所使用的关键字,标记了这个关键字的函数就是内联函数,不过只有标记“函数定义”时才是有效的,至于声明,标不标记inline都无所谓。由于内联函数和“宏”有点类似,而宏经常放在.h中,所以我们一般习惯于将“内联函数”的定义放在.h中,不过如果将函数放在.h中,以后可能会报重复定义的错误,所以我们往往需要将其修饰为static。
代码演示
inline.h
#ifndef INLINE_H
#define INLINE_H static inline int my_max(int a, int b) //inline只有修饰函数定义时才有效
{
a *= ;
b /= ;
return (a>b) ? a : b; //找出最大值
} #endif
main.c
#include <stdio.h>
#include "inline.h" int main(void)
{
int ret = ;
ret = my_max( ,);
printf("ret = %d\n", ret);
return ;
}
凡是要使用这个内联函数的.c,只需要包含这个.h即可。不过大家要小心,如果.c恰好有定义与“内联函数”同名的函数的话,不管这个函数普通函数还是“内联函数”,编译会报错的,所以编程时不要编写与内联函数同名的函数。
检查预编译后的.i文件
inline int my_max(int a, int b) //预编译后,这个玩意任然还在
{
a *= ;
b /= ; return (a > b) ? a : b;
} int main(void)
{
int ret = ; int (*funp) = my_max; ret = funp(, ); return ;
}
预编译后内联函数仍然还在,所以内联函数这玩意并不在“预编译阶段”被处理。
内联函数只是建议进行替换
指定了内联函数后,只是建议进行替换,到底会不会进行替换,这个需要看编译器,如果编译器判断存在如下情况的话,就算指定了inline关键字,但是编译器也只当做是普通函数。
①函数代码量很大,不够简短,代码超过5句以上时,就不简短了
②不是通过函数名调用的,而是通过“函数指针”来调用的
int (*funp) = my_max;
ret = funp(, );
③函数是递归
④包含switch、while、for、do while等
编译时无法识别inline关键字,怎么办
在解释原因之前,先说说c标准。
c标准
为了标准化c语法,所以制定了C标准,目前的c标准有4个版本
c89:1989年制定
c90:1990年制定
c99:1999年制定
c11:2011年制定
其实中间还有c94/c95标准,不过这两个可以忽略。c89和c90其实是一个标准,为什么一个标准有两个名字,这个是由于历史原因导致的。
一般来说,制定新标准时,一些旧的语法可能会被修改或者抛弃,然后再添加一些新的语法特性,至于inline则是从c99才开始支持的语法特性,所以目前只有c99/c11才支持inline。
为什么有些编译器在编译内联函数时,会有问题呢
说明你的编译器版本比较老,老版本默认是按照c89/90去编译的,自然不认识inline,所以我们需要给gcc指定-std=c99或者-std=c11选项,明确的告诉编译器,请使用c99、c11标准去编译c程序,这是就没问题了。
gcc a.c -std=c99 //或者c11
推荐使用gcc --version查一下版本号,我的Ubuntu gcc版本号gcc version 5.4.0 20160609,是支持inline的
C++——inline function的更多相关文章
- 什么是内联函数(inline function)
In C, we have used Macro function an optimized technique used by compiler to reduce the execution ti ...
- 内联扩展 inline expansion An Inline Function is As Fast As a Macro 与宏的比较
让编译器直接将完整的函数体插入到每一个调用该函数的地方,从而提高函数调用的运行速度. 优秀的JIT编译器会通过侦测运行信息,仅将需要频繁运行的瓶颈部分进行编译,从而大大削减编译所需的时间. 而且,利用 ...
- [C++] inline function
trap #define GET3(N) N*N*N GET3(1+2) : 1+2*1+2*1+2 = 7
- About Why Inline Member Function Should Defined in The Header File
About why inline member function should defined in the header file. It is legal to specify inline on ...
- Difference between Stored Procedure and Function in SQL Server
Stored Procedures are pre-compile objects which are compiled for first time and its compiled format ...
- C++ inline weak symbol and so on
关于inline这个关键字,听到强调得最多的是,它只是一种对于编译器的建议,而非强制执行的限定. 但事实上,即使这个优化最终由于函数太过复杂的原因没有达成,加上inline关键字(还有在类定义中直接定 ...
- Inline functions
Problems: (Page 372) There are two problems with the use of proprocessor macros in C++. The first is ...
- c++virtual inline 是否冲突
关于inline关键字:effective c++ item33:明智运用inlining.说到:inline指令就像register指令一样,只是对编译器的一种提示,而不是一个强制命令,意思是编译器 ...
- Understanding JavaScript Function Invocation and "this"
Understanding JavaScript Function Invocation and "this" 11 Aug 2011 Over the years, I've s ...
随机推荐
- 神啊,看看Log4Net这个东西吧
这个东西实在是让人感动的想哭囊…………-_-..... Log4Net.config文件 <?xml version="1.0" encoding="utf-8&q ...
- webpack 安装vue(两种代码模式compiler 和runtime)
使用webpack安装vue,import之后,运营项目报错,如下: [Vue warn]: You are using the runtime-only build of Vue where the ...
- robot:截图关键字
参考: https://www.cnblogs.com/hong-fithing/p/9656221.html--python https://blog.csdn.net/weixin_4315628 ...
- Swoole练习 websocket
WEBSOCKET 服务端代码 //创建websocket服务器对象,监听0.0.0.0:9502端口 $ws = new swoole_websocket_server("0.0.0.0& ...
- WCF之Windows宿主(可安装成服务自动并启动)
WCF之Windows宿主(可安装成服务自动并启动) 创建解决方案WCFServiceDemo 创建WCF服务库(类库或WCF服务库)WCFService ,添加引用System.ServiceMo ...
- 用pytorch1.0搭建简单的神经网络:进行回归分析
搭建简单的神经网络:进行回归分析 import torch import torch.nn.functional as F # 包含激励函数 import matplotlib.pyplot as p ...
- Linux01学习第一天 man
Linux标准的读音:哩呐科斯 Linux是一种类UNIX的系统,具有以下特点: 1.免费开源 2.模块化程度高 3.广泛的硬件支持 4.安全稳定 5.多用户,多任务(所以常应用于系统运维,以及合作开 ...
- 织梦/dedecms采集怎么去除a标签
dedecms采集去除a标签代码 DedeCMS采集规则-过滤-替换-技巧2009-01-14 15:491.采集去除链接[Copy to clipboard]CODE:{dede:trim}]*)& ...
- Linux中request_irq()中断申请与处理说明
1. 中断的理解 中断你可以理解为就是一种电信号,是由硬件设备产生的然后发送给处理器,处理器接收到中断后,就会马上向操作系统反映此信号,之后就是系统的工作了. 这里有两个注意的地方,第一中断是随时都 ...
- PowerBuilder学习笔记之删除和加载PBL文件的方法
删除PBL目录的方法:直接点删除键删除 加载PBL文件的方法:点Browse按钮选择PBL文件