zoukankan      html  css  js  c++  java
  • C++——inline function

    前言

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

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

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

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

    fun1 --> fun2 --> fun3 --> fun4 --> fun5 ---> ...

    在这些函数都没有返回之前,所有函数所消耗的栈空间,将一直不会被释放。如果复杂程序还有大量使用线程的话,线程函数还会占用栈空间。

    ②函数的调用过程,会多花费更多额外的时间

    调用函数时,除了函数代码本身执行的时间外,还需要花费额外的时间,比如:

    1)从调用位置跳转到函数代码处
    2)从栈中开辟空间,将形参、自动局部变量、返回地址压栈
    3)利用返回地址返回,以及返回时的弹栈

    这些都是需要额外时间的,特别是如果这个函数的调用非常频繁的话,累积花费的就更多。

    解决之道

    对于那些被频繁调用,而且“代码很简短”的函数来说,我们往往就使用“带参宏”和“内联函数”代替,以减少这类函数的数量,如此一来:

    1)函数减少了,在一定程度上节约了栈内存

    2)消除了函数调用所需的额外时间,效率更高

    C/C++都支持带参宏、内联函数

    带参宏

    参考

    宏——基础

    宏——高级

    代码演示

     1 #include <stdio.h>
     2         
     3 int my_max(int a, int b)
     4 {
     5     a *= 2; 
     6     b /= 3;
     7     return (a>b) ? a : b;  //找出最大值
     8 }
     9 
    10 int main(void)
    11 {
    12     int ret = 0;
    13     ret = my_max(25, 30);
    14     printf("ret = %d
    ", ret);
    15     return 0;
    16 }
    View Code

    如果my_max调用非常频繁的话,做成函数形式,其实是非常不划算的,所以完全可以使用带参宏来代替。

     1 #include <stdio.h>
     2 #define MY_MAX(a, b, ret)  
     3     ret = ((a*2)>(b/3)) ? (a*2) :(b/3)
     4 
     5 
     6 int main(void)
     7 {
     8     int ret = 0;
     9     MY_MAX(25, 30, ret);
    10     printf("ret = %d
    ", ret);
    11     return 0;
    12 }
    View Code

    进行宏替换后,main函数就变为了

    1 int main(void)
    2 {
    3     int ret = 0;
    4     
    5     ret = ((25*2)>(30/3)) ? (25*2) :(30/3);  //找出最大值
    6     printf("ret = %d
    ", ret);
    7     
    8     return 0;
    9 }
    View Code

    使用宏来代替后,“宏体”就变成了“引用者”的一部分,如此一来不仅节约了栈空间,也免去了函数调用时的额外开销。

    使用带参宏代替函数时的要求

    ①只用于对3~5行代码的简短函数进行替换,为什么只替换3~5行代码的简短函数呢?

    使用宏来代替函数,缺点也是很明显的,因为宏替换后,使用宏的函数的代码会增加,如果很多函数都有使用这个宏的话,程序的代码量就会急剧增加,毕竟代码也是需要存储空间的。为了节省点栈空间、以及降低“函数调用”所消耗的额外时间,结果导致整个程序代码的剧增,这就得不偿失了。

    ②而且只替换被频繁调用的函数

    替换这类函数才有明显的效果,否则占那么点小便宜也没有什么意思。

    带参宏的缺点

    带参宏的参数只用于替换,不涉及参数类型的检查,所以如果我们把参数写错了,在预编译进行宏替换时,是不会提示错误或者警告的,这不利于代码排错。

     1 #define MY_MAX(a, b, ret)  
     2     ret = ((a*2)>(b/3)) ? (a*2) :(b/3); 
     3 
     4 int main(void)
     5 {
     6     int ret = 0;
     7     MY_MAX(dsf344, 30, ret);
     8     printf("ret = %d
    ", ret);
     9     return 0;
    10 }
    View Code

    参数指定为dsf344肯定是错的,但是宏在替换时,只进行替换,不会进行参数检查,但是这个你要是搁在函数里面,函数会进行严格的参数检查,如果不对就会报错。为了改进带参宏不会进行参数检查的缺点,后来从c99标准开始就有了"内联函数"。

    内联函数

    内联函数是个啥

    内联函数既不是函数也不是宏,它一个兼具宏和函数共同特点的这么个特殊的玩意。

    ①具有宏的特点,会像宏一样进行替换

    不过宏是在“预处理阶段”进行替换的,而内联函数则是在“编译、链接”阶段进行替换的。替换的过程也被称为“内联”的过程,所以才被称为“内联函数”。

    ②也具有函数的特点,会像函数一样进行参数类型检查

    一句话来概括的话,内联函数就是会像函数一样进行“参数类型检查”的带参宏。

    内联函数举例

    inline关键字

    这个是内联函数所使用的关键字,标记了这个关键字的函数就是内联函数,不过只有标记“函数定义”时才是有效的,至于声明,标不标记inline都无所谓。由于内联函数和“宏”有点类似,而宏经常放在.h中,所以我们一般习惯于将“内联函数”的定义放在.h中,不过如果将函数放在.h中,以后可能会报重复定义的错误,所以我们往往需要将其修饰为static。

    代码演示

    inline.h

     1 #ifndef INLINE_H
     2 #define INLINE_H
     3 
     4 static inline int my_max(int a, int b) //inline只有修饰函数定义时才有效
     5 {
     6     a *= 2;
     7     b /= 3;
     8     return (a>b) ? a : b;  //找出最大值
     9 }
    10 
    11 #endif
    View Code

    main.c

     1 #include <stdio.h>
     2 #include "inline.h"
     3 
     4 int main(void)
     5 {
     6     int ret = 0;
     7     ret = my_max(25 ,30);
     8     printf("ret = %d
    ", ret);
     9     return 0;
    10 }
    View Code

    凡是要使用这个内联函数的.c,只需要包含这个.h即可。不过大家要小心,如果.c恰好有定义与“内联函数”同名的函数的话,不管这个函数普通函数还是“内联函数”,编译会报错的,所以编程时不要编写与内联函数同名的函数。

    检查预编译后的.i文件

     1 inline int my_max(int a, int b)  //预编译后,这个玩意任然还在
     2 {
     3     a *= 2;
     4     b /= 3;
     5 
     6     return (a > b) ? a : b;
     7 }
     8 
     9 int main(void)
    10 {
    11     int ret = 0;
    12     
    13     int (*funp) = my_max;
    14     
    15     ret = funp(25, 30);
    16     
    17     return 0;
    18 }
    View Code

    预编译后内联函数仍然还在,所以内联函数这玩意并不在“预编译阶段”被处理。

    内联函数只是建议进行替换

    指定了内联函数后,只是建议进行替换,到底会不会进行替换,这个需要看编译器,如果编译器判断存在如下情况的话,就算指定了inline关键字,但是编译器也只当做是普通函数。

    函数代码量很大,不够简短,代码超过5句以上时,就不简短了

    不是通过函数名调用的,而是通过“函数指针”来调用的

    1 int (*funp) = my_max;
    2 ret = funp(25, 30);
    View Code

    函数是递归

    包含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
    View Code

    推荐使用gcc --version查一下版本号,我的Ubuntu gcc版本号gcc version 5.4.0 20160609,是支持inline的

  • 相关阅读:
    最新seo优化技巧
    5ucms 调用当前文章的评论,以及评论列表
    5ucms的评论列表该怎么写
    Data Structure Binary Tree: Check for Children Sum Property in a Binary Tree
    Data Structure Binary Tree: Level order traversal in spiral form
    Data Structure Binary Search Tree: Inorder Successor in Binary Search Tree
    Algorithm: bit manipulation
    Algorithm: pattern searching
    Algorithm: dynamic programming
    Algorithm: inversion
  • 原文地址:https://www.cnblogs.com/kelamoyujuzhen/p/9694848.html
Copyright © 2011-2022 走看看