6

C语言内联函数,提升C技巧必备

 1 year ago
source link: https://www.51cto.com/article/746697.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文转载自微信公众号「混说Linux」,作者HunTalk_Linux。转载本文请联系混说Linux公众号。

内联函数是C语言从C++中借鉴过来的,适当的使用内联函数可以提高程序的执行效率。本篇文章就来讲解下内联函数,赶紧来看下吧!

一、函数调用

在讲内联函数之前,我们需要先了解函数调用,而函数调用,又不得不说函数调用的开销。

一个函数执行的时候,经常会调用另一个函数,比如执行函数A时,我们需要对一些数据进行处理,将运算结果暂存在R0寄存器,接着要调用另一个函数B,而函数B也用到了R0这个寄存器(用于保存函数的返回值),原本函数A暂存在R0寄存器的值就被改变了,这样做肯定不行。

现代计算机系统的做法都是会在执行函数B之前,先把R0寄存器的值保存到堆栈中,函数B执行结束后,再将堆栈中的值恢复到R0寄存器中,然后函数A继续执行,这样对于数据处理就不会有任何问题了。

但是,函数调用却消耗一定的时间进行切换,这段时间用来保存现场和恢复现场,大约相当于一两条语句的执行时间,这就是函数调用带来的开销。

图片

假如函数B很小,只有一两行代码,从上图我们可以看出,真正只有函数B执行代码的那段时间是对我们有用的,切换带来的就是额外的成本开销了,如果函数A里面多次调用函数B,那开销就更明显了。

图片

二、内联函数

函数B很小,又被频繁的调用,可能函数调用的切换时间比函数内代码的执行时间还长,这样明显划不来,那么我们就可以将这个函数声明为内联(加上 inline),编译器在编译时,会把内联函数的实现替换到每个调用内联函数的地方(可以与宏函数做类比),在调用处将代码展开,相当于自动将函数B的代码在调用它的地方复制了一份副本,没有了保护现场和恢复现场的时间,从而节省了函数调用的开销。

图片

内联函数一般要求如下:

1. 函数体积小,通常5行以内;

2. 被频繁调用;

3. 函数内无复杂的实现,比如:while、for循环,switch,递归等;

4. 函数没有包含静态变量。

来看一个简单的内联函数的例子:

#include <stdio.h>

// 将函数 max_value 声明为 inline
inline int max_value(int x, int y)
{
    return (x>y) ? x:y;
}

int main()
{
    int a = 1, b = 2;
    int m;
    m = max_value(a, b);
    
    return 0;
}

main函数代码在执行的时候是这样的:

int main()
{
    int a = 1, b = 2;
    int m;
    m = (1>2) ? 1:2;
    
    return 0;
}

内联函数在调用处展开了。

在c++ 中定义在类里面的函数,默认情况下都是内联的,比如下面这种情况:

#include <iostream>
using namespace std;
 
class HunTalk_Linux 
{
public:
    //默认是内联函数
    int max_value(int x, int y) 
    {
        return(x>y) ? x:y;
    }
};
 
int main() 
{
    return 0;
}

注意:函数声明为内联,仅仅是对编译器的建议,如果函数比较复杂,编译器会将其看做普通函数。

三、内联函数与宏

前面讲到可以与宏函数做类比,那么就纳闷了,为什么不直接定义一个宏,而是定义一个内联函数?存在即合理,自然有它存在的道理,相对于宏,内联函数提供了更好的方法:

参数类型检查。编译过程中,宏调用并不执行类型检查,甚至连正常参数也不检查,内联函数虽然具有宏的展开特性,但其本质仍是函数,编译器仍可以对其进行参数检查,而宏就不具备这个功能。

在宏中的编译错误很难发现,因为它们引用的是扩展的代码,而不是程序员键入的。

便于调试。内联函数代码的调试信息通常比扩展的宏代码更有用,它同样可以支持断点、单步......等调试功能。

接口封装。有些内联函数可以用来封装一个接口,而宏不具备这个特性。

引入内联函数主要是解决一些频繁调用的小函数造成额外时间开销的问题,但是也要在符合一定内联函数的情况下使用。

使用很多的内联函数,每个调用该函数的地方都需要替换成函数体,代码量就会增加,代码量就会增加也同时带来了潜在的编译时间的增加。

算法里面有个概念叫空间换时间,就是使用内存占用更大的算法换取执行速度的提升,所以说适当的使用内联函数可以提高程序的执行效率。

本文转载自微信公众号「​​混说Linux​​」,可以通过以下二维码关注。转载本文请联系混说Linux公众号。

1bcb15bd396d25d86725e246ced94984.jpg


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK