18. 内联函数

调用函数,需要有额外开销的.

int f(int i)
{
   return i*2;
}

main()
{
  int a = 2;
  int b = f(a);
  return 0;
}

汇编语言:
_f_int:
  add ax,@sp[-8],@sp[-8]
  ret
_main:
  add sp,#8 
  //sp寄存器(堆栈指针) #8立即数 程序运行过程中,每个程序有独立的堆栈,放本地变量和返回地址. 
  mov ax,#4 //
  mov @sp[-8],ax
  mov ax,@sp[-8]
  push ax
  call _f_int
  mov @sp[-4],ax
  pop ax       

过程如下:
1. add sp, #8 //堆栈的指针往上移动了8, 这8个字节放a和b这两个变量。给本地分配空间完成.

这里写图片描述

  1. mov ax, #4
    ax是一个寄存器,对于x86来说,ax主要是做运算的寄存器. 让ax计算器等于4.
    这里写图片描述

  2. mov @sp[-8], ax
    把ax移动sp-8的地方去, 相当于a = 4.
    这里写图片描述

  3. move ax, @sp[-8] //把sp-8又move到了ax , 等价于a变量赋给ax这件事.

  4. push ax //把a赋给函数i, 把ax寄存器里面的值push到堆栈里去
    //函数的参数和本地变量都在堆栈里.
    //其中4就是函数的参数i
    这里写图片描述
  5. call _f_int
    主要做2件事情.
    6.1. 把下一条指令的地址push到堆栈里面去,
    这里写图片描述
    6.2. 转到调用函数f(int)中去.
    这里写图片描述
    此时sp -4 是它的返回地址,sp-8是i参数.
    这里写图片描述
    6.3. add ax, @sp[-8], @sp[-8]
    把sp-8 + sp-8放到ax中去. 此时ax =8, 也就是返回的值.
    这里写图片描述
    6.4. move @sp[-4], ax
    这里写图片描述
    return的结果是由ax带回来的,
    6.5. pop ax
    这里写图片描述

//调用函数的时候,需要做以下几件事:
(1). 把函数的参数送到堆栈里
(2). 把返回地址推到堆栈里面去
(3). 准备返回的值
(4). push所有的东西都要pop出来

这么多额外的东西,如何避免?
C++提供内联函数解决这些问题.

inline函数: 当去调用inline函数时, 不是真的去做push call pop这些动作. 而是把那些函数的代码嵌入到调用它的地方去. 但是仍然保持它函数的独立性.

//程序的基本流程图如下:
预处理(.i) -> 编译(.s) -> 汇编(.o) -> 链接(.exe)
预处理:(1). 读取C/C++源程序,对其中的伪指令(以#开头的指令)进行处理. #define #if #ifdef #include 等
(2). 删除所有的注释
(3). 添加行号和文件名标识
(4). 保留所有的#pragma编译器指令
编译:将预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后,禅城相应的汇编代码文件.
汇编:将编译完的汇编代码翻译成机器指令,并生成可重定位目标程序的.o文件,该文件为二进制文件,字节编码是机器指令.
链接:将一个目标文件(库文件)链接在一起生成一个完整的可执行程序.

   inline int f(int i)
   {
     return i*2;
   }
   main()
   {
      int a = 4;
      int b = f(a);
   }
 生成的代码实际上这样:
 main()
 {
    int a = 4;
    int b = a+a;
 }
 汇编:
 _main()
 {
   add sp,#8
   mov ax,#4
   mov @sp[-8],ax
   add ax, @sp[-8], @sp[-8]
   mov @sp[-4], ax
   return 0;
 }
.h .cpp必须重复inline.
.cpp:产生函数
.h:调用函数看的
.cpp不放inline,编译器会认为不是inline,编译器会真的可执行文件中留下这个函数.
.h inline,编译它调用的时候,不能生产调用的代码,需要插进来.
//a.h
inline void f(int i, int j);

//a.cpp
#include "a.h"
#include <iostream>
using namespace std;
inline void f(int i,int j)
{
   cout << i << " " << j << endl;
}

//main.cpp
#include "a.h"
int main()
{
  f(10,8);
  return 0; //程序出错
}
//同一个时刻,编译器只能看到一个文件.
//当编译main.cpp时候,只能看到inline f(); f(10,8).但不知道f长什么样子.
//当编译a.cpp时候,由于看到inline f() {}, 不需要产生任何代码. 

解决方案: 把inline函数的定义,也放在.h中

//a.h
#include <iostream>
using namespace std;
// inline int f(int i,int j); 此行可省略

inline int f(int i, int j)  //此函数前面加了inline,实际上相当于声明,而不是定义了. 
{
  cout << i << " " << j << endl;
}

//main.cpp
#include "a.h"
int main()
{
  f(4,5);
  return 0;
}

内联函数需要把原型和定义放在.h中,其中原型可省略. 而实际上函数定义加了inline,相当于声明

使用inline的优缺点:
1. 若程序中有好几处需要调用inline函数,就意味着程序会变长. 牺牲代码的空间,但是会降低代码额外的开销. 这是一种典型的空间换时间的策略.
使用宏也可以,但是不能做类型检查的. 建议使用inline函数代替宏.

#define f(a) (a)+(a)
main()
{
  double a = 4;
  printf("%d\n", f(a));
  return 0;
}
inline int f(int i)
{
   return i*2;
}
main()
{
   double a = 4;
   printf("%d\n", f(a));
}

2.若发现inline函数过于巨大,则可能会拒绝. 递归不能inline.
如果是成员函数在class的声明时, 就给出函数的body, 那些函数统统都是inline.
方法一: 在class定义内部

class Point
{
   private:
     int color;
   public:
     int getColor() { return color; } //内联函数
     void setColor(int color) {
        this->color = color; //内联函数
     }
};

方法二:在class定义外部,.h中,成员函数定义前面加inline.

class Rectangle
{
   int width, height;
   public:
     Rectangle(int w = 0, int h = 0);
     int getWidth() const;
     void setWidth(int w);
     int getHeight() const;
     void setHeight(int h);
};

inline Rectangle::Rectangle(int w, int h):width(w),height(h) //内联函数
{}

inline int Rectangle::getWidth() const
{ return width; }

inline or not?

1 . 若函数比较小,可以考虑做成inline.
2. 如果是循环里面频繁调用的函数, 值的把它做成inline. 因为循环调用,意味着大量的堆栈操作,所以做成inline是非常值得的.
3. 超过20行的代码,不建议inline.
4. 递归的函数不建议用inline.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值