C++之内联函数

简介: C++之内联函数

前言

函数调用要开辟栈帧,如果是一些稍微复杂的递归问题或者排序问题(含有交换比较多,例如快排)就会导致开辟的函数栈帧的数量太多了,那么有没有什么办法可以优化一下这个函数栈帧呢?


一、宏

对于前言中的问题,C语言给出的办法是——宏。

宏定义的函数,在预处理阶段就会将函数与程序中对应的语句进行替换,进而优化了多次调用函数所开辟的函数栈帧。既然C语言中有优化这个问题的方法,那么我们的C++为什么还要创造一种新方法呢?

我们先来回顾一下宏的优缺点:

1.宏的优缺点

(1)优点

①增强代码的复用性。

②提高性能。

(2)缺点

①不方便调试宏。(因为宏是在预编译阶段进行替换,无法调试)

②没有类型安全的检查。

③宏会导致代码可读性差,可维护性差,容易误用(易出错)。

2.C++中替代宏的方法

由于宏有这三个缺点,C++中给出了替代宏的方法:

(1)常量定义换用const enum

(2)短小函数定义换用内联函数

其中的const enum是C语言中就有的,内联函数却是C++新给出的概念。那么,接下来我们就来了解一下内联函数是什么,以及它为什么可以替换宏。

二、内联函数

使用inline关键字修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,因此没有函数调用建立栈帧的开销,进而提升程序运行的效率。

1.内联函数与宏函数的联系和区别

(1)联系

内联函数的作用和宏是一样的,都是将函数直接替换进程序,进而避免了函数栈帧。

(2)区别

因为内联函数的替换过程是在程序运行起来以后,所以可以进行调试,方便观察;

因为内联函数是直接在程序中展开,和其他函数是一样的,所以内联函数的参数类型是受限制的

由此可见内联函数是优化了宏的缺点,同时具有宏的功能

2.内联函数的特性

(1)内联函数是一种以空间换时间的做法

用函数体替换函数调用

(2) inline(内联函数)对编译器而言只是个建议,但是编译器不一定会采纳这个建议。

inline是建议编译器将 inline修饰的函数展开,但根据不同情况编译器具体决定是进行展开还是函数调用。

①如果内联函数是一个短函数(代码量较短),则编译器会将它展开,正常使用;

②如果内联函数是一个长函数(代码量较长),则编译器不会将它展开,而是用函数调用的方式使用这个函数。(如果内联函数是一个递归函数,也可能不会被展开)

为什么长函数不展开?

答:为了避免代码膨胀(代码膨胀是指代码有着不必要的长度、缓慢或者其他浪费资源的情况),会影响可执行程序的大小,进而影响安装包的大小(例子:更新软件需要安装包,如果出现代码膨胀,安装包的大小就会变大)

如何观察编译器是否对内联函数进行进行展开?

答:

1.在release模式下,查看编译器生成的汇编代码中是否存在call Add

2.在debug模式下,需要对编译器进行设置,否则不会展开。设置以后进行调试,转到汇编代码,就可以观察该内联函数在程序运行过程中是否被展开。

debug模式下,编译器默认不会对代码进行优化(方便调试),以下给出vs2013环境下的设置方式。

举个例子:

普通函数

int ADD(int x, int y)//普通函数
{
  return x + y;
}
int main()
{
  int ret = 0;
  ret = ADD(10, 20);
  return 0;
}

汇编代码:

内联函数

inline int ADD(int x, int y)//内联函数
{
  return x + y;
}
int main()
{
  int ret = 0;
  ret = ADD(10, 20);
  return 0;
}

汇编代码:

内联函数是否可以是递归函数?

一般来说,内联函数的机制用于优化规模小、流程直接、频繁调用的函数,很多编译器不支持内联递归函数,而且一个代码量太大的函数也不大可能在调用点内联地展开。

(3)内联函数不建议声明和定义分离

内联函数的声明和定义分离会导致链接错误,所以使用内联函数就直接在该源文件中定义即可。

为什么内联函数不能声明和定义分离?

答:内联函数在编译期间是不会生成地址的(编译器认为内联函数不需要函数地址,因为内联函数的使用方法,就是直接在程序内部展开,而不会进行函数调用。inline的修饰会让编译器觉得这个函数他就是个内联函数,所以无论它的代码长与短,在编译期间,编译器都不会给他生成函数地址。),因此,在编译期间源文件所生成的符号表中找不到内联函数,链接时就无法在符号表中找到对应的函数,就会导致链接错误。

例子:

当内联函数的声明和定义分离时

// test.h文件
#include <iostream>
using namespace std;
inline void f(int i);
// test.cpp文件
#include "test.h"
void f(int i)
{
cout << i << endl;
}
// main.cpp文件
#include "test.h"
int main()
{
f(10);
return 0;
}

3.内联函数的优势和缺陷

(1)优势

减少了调用开销,提高了效率。

(2)缺陷

可能使目标文件变大(因为是将函数直接进行展开,所以会增加代码量)


总结

以上就是今天要讲的内容,本文介绍了宏和内联函数的相关概念,主要介绍了内联函数的特性。本文作者目前也是正在学习C++相关的知识,如果文章中的内容有错误或者不严谨的部分,欢迎大家在评论区指出,也欢迎大家在评论区提问、交流。

最后,如果本篇文章对你有所启发的话,希望可以多多支持作者,谢谢大家!

相关文章
|
21天前
|
存储 安全 编译器
【C++专栏】C++入门 | 函数重载、引用、内联函数
【C++专栏】C++入门 | 函数重载、引用、内联函数
27 0
|
2月前
|
安全 编译器 Linux
【C++练级之路】【Lv.1】C++,启动!(命名空间,缺省参数,函数重载,引用,内联函数,auto,范围for,nullptr)
【C++练级之路】【Lv.1】C++,启动!(命名空间,缺省参数,函数重载,引用,内联函数,auto,范围for,nullptr)
|
2天前
|
安全 编译器 程序员
【C++入门】内联函数、auto与基于范围的for循环
【C++入门】内联函数、auto与基于范围的for循环
|
15天前
|
存储 安全 编译器
【C++成长记】C++入门 |函数重载、引用、内联函数
【C++成长记】C++入门 |函数重载、引用、内联函数
|
22天前
|
存储 编译器 Linux
【C++】C++入门第二课(函数重载 | 引用 | 内联函数 | auto关键字 | 指针空值nullptr)
【C++】C++入门第二课(函数重载 | 引用 | 内联函数 | auto关键字 | 指针空值nullptr)
|
24天前
|
存储 编译器 C语言
我的C++奇迹之旅:内联函数和auto关键推导和指针空值
我的C++奇迹之旅:内联函数和auto关键推导和指针空值
|
2月前
|
编译器 C语言 C++
【C++】内联函数
【C++】内联函数
23 1
|
5月前
|
存储 安全 编译器
【C++】内联函数、auto关键字、基于范围的for循环、指针空值nullptr
【C++】内联函数、auto关键字、基于范围的for循环、指针空值nullptr
38 0
|
2月前
|
存储 安全 编译器
【c++入门】引用,内联函数,auto
朋友们大家好,本节我们来到c++中一个重要的部分:引用
【c++入门】引用,内联函数,auto
|
2月前
|
编解码 编译器 API
【C++ 内联函数和库】了解函数导出至库的原理以及其中内联函数的处理
【C++ 内联函数和库】了解函数导出至库的原理以及其中内联函数的处理
39 0