[C++]——内联函数

内联函数
一、概述

        函数是一个可以重复使用的代码块,CPU 会一条一条地挨着执行其中的代码。CPU 在执行主调函数代码时如果遇到了被调函数,主调函数就会暂停,CPU 转而执行被调函数的代码;被调函数执行完毕后再返回到主调函数,主调函数根据刚才的状态继续往下执行。

        一个 C/C++程序的执行过程可以认为是多个函数之间的相互调用过程,它们形成了一个或简单或复杂的调用链条,这个链条的起点是 main(),终点也是 main()。当 main() 调用完了所有的函数,它会返回一个值(例如return 0;),来结束自己的生命,从而结束整个程序

        函数调用是有时间和空间开销的。程序在执行一个函数之前需要做一些准备工作,要将实参局部变量返回地址以及若干寄存器压入栈中,然后才能执行函数体中的代码;函数体中的代码执行完毕后还要清理现场,将之前压入栈中的数据都出栈,才能接着执行函数调用位置以后的代码。

        如果函数体代码比较多,需要较长的执行时间,那么函数调用机制占用的时间可以忽略;如果函数只有一两条语句,那么大部分的时间都会花费在函数调用机制上,这种时间开销就就不容忽视。

        为了消除函数调用的时空开销,C++ 提供一种提高效率的方法,即在编译时将函数调用处用函数体替换,类似于C语言中的宏展开。这种在函数调用处直接嵌入函数体的函数称为内联函数(Inline Function),又称内嵌函数或者内置函数。

二、注意:

        当我们声明一个函数为内联时,我们是在请求编译器尝试将该函数的代码在每个调用点内联展开。然而,编译器可以选择忽略这个请求,特别是如果函数体很大,或者函数中包含复杂的控制结构(如循环、递归等),编译器可能会决定不进行内联

总结:编译器在处理内联函数时,主要关注的是函数的定义(包括函数体),因为编译器需要这些信息来决定是否将函数内联到每个调用点。尽管我们可以声明一个函数为内联,但编译器有权根据函数的具体实现和上下文环境决定是否真正进行内联。

例:

错误1:******************************************************************************
include<iostream>
using namespace std;
inline void swap(int *a,int *b);    //声明内联函数

void swap(int *a,int *b)            //定义内联函数
    int temp;
    temp = *a;
    *a  = *b;
    *b = temp;
}
int main()
{
    int m;    
    int n;
    cin>>m>>n;
    cout<<m<<","<<n<<endl;
    swap(&m,&n);
    cout<<m<<","<<n<<endl;
    return 0;
}
错误2:*******************************************************************************
#include <iostream>
using namespace std;
 

void swap1(int *a, int *b); //声明内联函数
 
int main()
{
    int m, n;
    cin>>m>>n;
    cout<<m<<", "<<n<<endl;
    swap1(&m, &n);
    cout<<m<<", "<<n<<endl;
 
    return 0;
}
 
//定义内联函数
inline void swap1(int *a, int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

正确:****************************************************************************

include<iostream>
using namespace std;
inline void swap(int *a,int *b)     //定义内联函数
{
    int temp;
    temp = *a;
    *a  = *b;
    *b = temp;
}
int main()
{
    int m;    
    int n;
    cin>>m>>n;
    cout<<m<<","<<n<<endl;
    swap(&m,&n);
    cout<<m<<","<<n<<endl;
    return 0;
}

注:要在函数定义处添加 inline 关键字,在错误1示例中:在函数声明处添加 inline 关键字虽然没有错,但这种做法是无效的,编译器会忽略函数声明处的 inline 关键字。在错误2示例中:由于内联函数比较短小,我们通常的做法是省略函数原型,将整个函数定义(包括函数头和函数体)放在本应该提供函数原型的地方。

在编译器遇到函数swap(&m,&n);会用swap()函数代码替换swap(&m.&n),同时用实参代替形参,以下为替换后的结果:

int temp;
temp = *(&m);
*(&m) = *(&n);
*(&n) = temp;

代码编译器会将*(&m)和*(&n),优化成m,n;

内联函数使用优缺点:
一、优点:

    当函数比较复杂时,函数调用的时空开销可以忽略,大部分的 CPU 时间都会花费在执行函数体代码上,所以我们一般是将非常短小的函数声明为内联函数。 以此消除函数调用的时空开销,提高效率。

二、缺点:       

        使用内联函数的缺点也是非常明显的,编译后的程序会存在多份相同的函数拷贝,如果被声明为内联函数的函数体非常大,那么编译后的程序体积也将会变得很大。

三、建议:

        对函数作 inline 声明只是程序员对编译器提出的一个建议,而不是强制性的,并非一经指定为 inline 编译器就必须这样做。编译器有自己的判断能力,它会根据具体情况决定是否这样做。所以再次强调,一般只将那些短小的、频繁调用的函数声明为内联函数。

内联函数使用:

一、为什么不把所有的函数都定义成内联函数呢?
  1. 内联函数以代码膨胀(拷贝)作为代价,仅仅省略函数使用开销,从而提高程序的执行效率。(开销:参数压栈跳转出栈返回操作)。
  2. 如果执行函数体内代码的时间比函数调用的开销大得多,那么inline效率收益会很小。
  3. 每一处内联函数的调用都要拷贝代码,使程序的总代码量增大,消耗更多的内存空间。

二、不宜使用内联:
  1. 如果函数体内代码比较长,使用内联将导致可执行代码膨胀过大。
  2. 如果函数体内出现循环或者其他复杂的控制结构,那么执行函数体内代码的时间将比函数调用的开销大得多。
  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值