前言

当代码写复杂后,一定会封装出大量的函数,这会导致两个问题:

①函数越多,栈的消耗也越厉害

疑问:为什么代码复杂了、函数变多了,栈消耗的就很厉害?

:因为这会导致函数的调用深度可能会很深,比如:

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的更多相关文章

  1. 什么是内联函数(inline function)

    In C, we have used Macro function an optimized technique used by compiler to reduce the execution ti ...

  2. 内联扩展 inline expansion An Inline Function is As Fast As a Macro 与宏的比较

    让编译器直接将完整的函数体插入到每一个调用该函数的地方,从而提高函数调用的运行速度. 优秀的JIT编译器会通过侦测运行信息,仅将需要频繁运行的瓶颈部分进行编译,从而大大削减编译所需的时间. 而且,利用 ...

  3. [C++] inline function

    trap #define GET3(N)  N*N*N GET3(1+2) :  1+2*1+2*1+2 = 7

  4. 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 ...

  5. 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 ...

  6. C++ inline weak symbol and so on

    关于inline这个关键字,听到强调得最多的是,它只是一种对于编译器的建议,而非强制执行的限定. 但事实上,即使这个优化最终由于函数太过复杂的原因没有达成,加上inline关键字(还有在类定义中直接定 ...

  7. Inline functions

    Problems: (Page 372) There are two problems with the use of proprocessor macros in C++. The first is ...

  8. c++virtual inline 是否冲突

    关于inline关键字:effective c++ item33:明智运用inlining.说到:inline指令就像register指令一样,只是对编译器的一种提示,而不是一个强制命令,意思是编译器 ...

  9. Understanding JavaScript Function Invocation and "this"

    Understanding JavaScript Function Invocation and "this" 11 Aug 2011 Over the years, I've s ...

随机推荐

  1. LeetCode_371. Sum of Two Integers

    371. Sum of Two Integers Easy Calculate the sum of two integers a and b, but you are not allowed to ...

  2. 【设计】IOT设备控制页面设计

    https://www.uishe.cn/10803.html https://huaban.com/pins/1012512760/ https://huaban.com/pins/10878772 ...

  3. web端自动化——自动化测试准备工作

    准备工作# 在开始自己项目的自动化测试之前,我们最好已经完成了下面的准备工作: 1.熟悉待测系统 对项目的待测系统整体功能和业务逻辑有比较清晰的认识. 2.编写系统的自动化测试用例大纲和自动化测试用例 ...

  4. 走进热修复框架AndFix的世界

    关于阿里的开源热修复框架AndFix引起了广泛共鸣,受到了很多人的青睐.那今天就跟随我的步伐来详细了解一下AndFix的详细信息和使用方法.1.什么是AndFix? AndFix是阿里巴巴出的一个专门 ...

  5. 线性链条件随机场(CRF)的原理与实现

    基本原理 损失函数 (线性链)CRF通常用于序列标注任务,对于输入序列\(x\)和标签序列\(y\),定义匹配分数: \[ s(x,y) = \sum_{i=0}^l T(y_i, y_{i+1}) ...

  6. php 文字转换成拼音

    <?php //中文字转拼音 $d=array( array("a",-20319), array("ai",-20317), array("a ...

  7. spring 整合guava

    一.ApplicationContext.xml中的配置 <!--开启缓存注解--> <cache:annotation-driven /> <bean id=" ...

  8. [转帖]ARM发布Ethos-N57/N73 NPU、Mali-G57 Valhall GPU和Mali-D37 DPU

    ARM发布Ethos-N57/N73 NPU.Mali-G57 Valhall GPU和Mali-D37 DPU https://www.cnbeta.com/articles/tech/902417 ...

  9. [转帖]MySQL语句大全

    MySQL语句大全 https://www.cnblogs.com/jicki/p/5548676.html 一.连接mysql. 格式: mysql -h主机地址 -u用户名 -p用户密码 二.修改 ...

  10. (二)linux 学习 -- 探究操作系统

    The Linux Command Line 读书笔记 - 部分内容来自 http://billie66.github.io/TLCL/book/chap04.html 文章目录 ls 命令进阶 `l ...