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++相关的知识,如果文章中的内容有错误或者不严谨的部分,欢迎大家在评论区指出,也欢迎大家在评论区提问、交流。

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

相关文章
|
6月前
|
存储 安全 编译器
【C++专栏】C++入门 | 函数重载、引用、内联函数
【C++专栏】C++入门 | 函数重载、引用、内联函数
53 0
|
1月前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
1月前
|
存储 编译器 程序员
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(二)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
1月前
|
程序员 C++ 开发者
C++入门教程:掌握函数重载、引用与内联函数的概念
通过上述介绍和实例,我们可以看到,函数重载提供了多态性;引用提高了函数调用的效率和便捷性;内联函数则在保证代码清晰的同时,提高了程序的运行效率。掌握这些概念,对于初学者来说是非常重要的,它们是提升C++编程技能的基石。
21 0
|
3月前
|
安全 编译器 C++
C++入门 | 函数重载、引用、内联函数
C++入门 | 函数重载、引用、内联函数
32 5
|
2月前
|
C语言 C++
C++(三)内联函数
本文介绍了C++中的内联函数概念及其与宏函数的区别。通过对比宏函数和普通函数,展示了内联函数在提高程序执行效率方面的优势。同时,详细解释了如何在C++中声明内联函数以及其适用场景,并给出了示例代码。内联函数能够减少函数调用开销,但在使用时需谨慎评估其对代码体积的影响。
|
4月前
|
存储 安全 编译器
【C++入门 四】学习C++内联函数 | auto关键字 | 基于范围的for循环(C++11) | 指针空值nullptr(C++11)
【C++入门 四】学习C++内联函数 | auto关键字 | 基于范围的for循环(C++11) | 指针空值nullptr(C++11)
|
5月前
|
存储 编译器 C语言
【C++入门】—— C++入门 (下)_内联函数
【C++入门】—— C++入门 (下)_内联函数
28 2
|
5月前
|
C语言 C++ 编译器
【C++语言】冲突-C语言:输入输出、缺省参数、引用、内联函数
【C++语言】冲突-C语言:输入输出、缺省参数、引用、内联函数
【C++语言】冲突-C语言:输入输出、缺省参数、引用、内联函数
|
5月前
|
存储 安全 编译器
C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字
C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字
42 2