【C++入门】内联函数

news2024/11/28 18:50:18

个人主页:平行线也会相交
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创
收录于专栏【C++之路】
在这里插入图片描述

目录

  • 什么是内联函数
  • 内联函数特性

什么是内联函数

内联函数概念:

内联函数就是以inline修饰的函数叫做内联函数,编译时会在调用内联函数的地方展开,没有函数调用占用建立栈帧的开销。

我们知道函数的调用是有消耗的,比如函数调用会建立栈帧(在栈上开辟一块空间)。
比如:

int Add(int x, int y)
{
	return (x + y) * 10;
}
int main()
{
	for (int i = 0; i < 10000; i++)
	{
		cout << Add(i, i + 1) << endl;
	}
	return 0;
}

上述代码中我们建立了10000个Add函数的栈帧,在C语言中可以使用宏函数来解决建立如此多的函数栈帧。C语言的解决方式是这样的,即:#define Add(x,y) ((x)+(y))一定要注意xy是一个表达式,所以C语言宏函数的这种解决方式其实是容易出错的,只要稍微不注意的话就会很容易出错而且宏函数不可以进行调试;同时宏函数的一个优势就是不需要建立栈帧,提高了调用效率。而对于C++而言,为了解决C语言的这种缺陷,C++提供的解决方案就是内联函数

内联函数的关键字是Inline

内敛函数使用起来也是非常的方便。就不如说上面代码中我们调用了10000次Add函数,下面请看内联函数使用样例:

inline int Add(int x, int y)
{
	return (x + y);
}

内联函数相对于C语言宏函数的解决方法而言就显得非常有优势:

1.不需要建立函数栈帧。
2.可读性强,且不易出错。
3.可以调试
4.使用起来简单。

然而并不是所有的函数都是可以使用内联函数的。内联函数只适用于短小的且频繁调用的函数。太长的函数如果使用内联函数的话会导致代码屏障

所以我们并不是要把所有的函数都弄成为内联函数,内联函数使用于短小而频繁调用的函数。如果函数比较长、又或者是递归函数不要让其称为内联函数。

内联函数特性

1.内联函数会在编译阶段用函数体替换函数调用。缺陷是可能会使可执行程序(或者目标文件)变大,优势就是减少了函数调用的开销,提高了运行效率。
2.内联函数只是编译器给我们的一个建议,最终函数是否会成为内联函数取决于编译器自己。如果我们把递归函数、比较长的函数加上inline的话就会被编译器否决掉。我们可以这样理解,内联函数只是我们向编译器发出的一个请求,然而编译器可以接收这个请求,当然也完全可以忽略这个请求。
3.内联函数不建议声明和定义分离,如果分离的话就会导致链接错误,因为inline被展开,此时就没有函数地址了,链接就会找不到。

现在有一个func函数,这个func函数经过编译后有50行指令,倘若在某个项目中总共需要调用10000次func函数。来看看是否使用内联函数的区别。

情况1:如果func函数不是内联函数:总共需要指令10000+50行,每次调用都是直接跳转到func函数去执行func函数(即call func(0x11223344)),每次跳转过去执行的是一样的,所以是+而不是*
情况2:那如果func函数是内联函数,要把这50行指令展开放到调用的地方,即需要10000*50行指令。这样就会导致可执行程序变大

我们以下面这段代码为例,看看如果加上inline会如果,如果不加inline又会如何。
在这之前我们首先要知道:

在Debug模式下,需要我们对编译器进行设置,否则内联函数不会展开,因为默认在Debug环境下不会对代码进行优化。

#include<iostream>
using namespace std;

inline int Add(int x, int y)
{
	return (x + y);
}
int main()
{
	for (int i = 0; i < 10000; i++)
	{
		cout << Add(i, i + 1) << endl;
	}
	return 0;
}

我们来看一下编译器是否会支持我们把Add函数弄为Inline

在这里插入图片描述

我们发现这里出现call Add(07FF76C5E13CAh),发现Add函数并没有称为Inline。这就是因为默认Debug模式下内联不会起作用。所以我们要调一调属性:

在这里插入图片描述

然后依然是在Debug模式下进行调试,请看:

在这里插入图片描述
现在我们发现并没有出现call Add,因为Add函数展开了,此时Add函数就是内联函数
现在我们改一下Add函数的内容(但依然加上Inline)使得编译器认为Add函数不应该是内联函数,我们把Add函数改成这样,请看:

inline int Add(int x, int y)
{
	cout << "abca" << endl;
	cout << "abca" << endl;
	cout << "abca" << endl;
	cout << "abca" << endl;
	cout << "abca" << endl;
	cout << "abca" << endl;
	cout << "abca" << endl;
	return (x + y);
}

我们判断一下此时虽然我们在Add函数前加上了Inline,但此时编译器真的认为Add函数是内联函数吗?请看:

在这里插入图片描述
我们可以看到这里并没有把Add函数进行展开,故Add函数此时并不是内联函数,编译器认为Add函数太长了,不应该把Add函数弄成内联函数,否则就可能出现代码屏障,进而导致最后的可执行程序(或者目标文件)变得非常大。就算我们在Add函数前加上Inline也依旧不会起作用。

我们来看看内联函数的特性3:内联函数不建议声明和定义分离,如果分离的话就会导致链接错误,因为inline被展开,此时就没有函数地址了,链接就会找不到。

这里举个例子:

//main().cpp
#include"func.h"
int main()
{
	func(100);
	return 0;
}


//func.cpp
#include"func.h"
void func(int i)
{
	cout << i << endl;
}

//func.h
#include<iostream>
using namespace std;
inline void func(int i);

运行结果如下:
在这里插入图片描述
这里编译没有出现问题,而是链接出现了问题,即链接不上,为什么这里明明有func函数的定义却找不到呢?
程序在进行编译之后会进行链接,因为只有声明没有定义,但是声明的时候发现func函数是一个内联函数,那好,既然是内联函数那就展开就好了,但是问题又来了,我们想展开这个内联函数却又发现展不开,因为这里只有声明,于是编译器只能call函数func的地址,于是拿着修饰函数名?func@@YAXH@Z去符号里找发现又找不到,因为内联函数不会生成符号表,更不会有地址(内联函数认为在需要调用自己的时候直接展开了)。故在链接阶段就找不到函数func的定义。所以内联函数声明和定义不能进行分离,还是那句话:内联函数不会生成符号表,更不会有地址(因为内联函数认为自己会直接进行展开,根本不需要被call。所以链接的时候找不到函数func的定义,就直接报错了。

我们应该这样写内联函数(不要声明和定义分离):

//main().cpp
#include"func.h"
int main()
{
	func(100);
	test();
	return 0;
}


//func.cpp
#include"func.h"
void test()
{
	func(100);
}

//func.h
#include<iostream>
using namespace std;

//inline void func(int i);

inline void func(int i)
{
	cout << i << endl;
}
void test();

运行结果如下:
在这里插入图片描述
好了,以上就是C++中的内联函数的介绍。
到这里啦,再见各位!!!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/474322.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

No.051<软考>《(高项)备考大全》【冲刺5】《软考之 119个工具 (3)》

《软考之 119个工具 &#xff08;3&#xff09;》 41.进度计划编制工具:42.绩效审查:43.卖方投标分析:44.质量成本:45.成本汇总:46.历史关系:47.资金限制平衡:48.挣值管理:49.预测:50.完工尚需绩效指数:51.成本效益分析:52.试验设计:53.七种基本质量工具:54.统计抽样:55.其他质…

Linux拓展:链接库

一.说明 本篇博客介绍Linux操作系统下的链接库相关知识&#xff0c;由于相关概念已在Windows下链接库一文中介绍&#xff0c;本篇博客直接上操作。 二.静态链接库的创建和使用 1.提前看 这里主要介绍的是C语言的链接库技术&#xff0c;而在Linux下实现C语言程序&#xff0c…

Web入门脚本三:一键完成与dex的交互,羊毛党必备

前言 该脚本用途&#xff1a;一键可以完成与dex的所有交互&#xff0c;包括2次swap&#xff0c;添加/移除流动性&#xff0c;以及farm和提取LP。一次运行可以有6条交易记录。 无论是个人单刷还是羊毛党批量地址刷交互都完美适配。当然反女巫方案不在这次文章的讨论范围内。 一、…

Python快速入门,看这一篇就够了

大家好&#xff0c;我是老三&#xff0c;我最近在看一些人工智能相关的东西&#xff0c;大部分相关的框架&#xff0c;都是用Pyhon写的。 老三会用Python&#xff0c;但谈不上多熟练&#xff0c;最近准备拿Python作为自己的第二语言&#xff0c;那么这期我们来一起快速梳理一下…

程序员该如何学习技术

程序员该如何学习技术 前言 学习是第一生产力&#xff0c;我从来都是这么认为的&#xff0c;人只有只有不断地学习才能意识到自己的缺点和不足&#xff0c;身为程序员&#xff0c;我更认为人们应当抱着终身学习的想法实践下去&#xff0c;这是我所一直践行且相信的。 高处不胜寒…

体验 GPT-4 后,回顾 OpenAI 发展历程及感悟

从 ChatGPT Plus 发布第一天就开始重度使用&#xff0c;刚刚和新发布的 GPT-4 进行了 20 多轮对话&#xff0c;来简单介绍下这几个模型背后的技术&#xff0c;并且分享下感受。 GPT 在发展历程中&#xff0c;一共经历了 4 个阶段&#xff0c;分别是 1、2、3、4。这几个阶段分别…

【VM服务管家】VM4.x算法模块开发_4.3 联合Halcon开发

目录 4.3.1 联合开发&#xff1a;集成HALCON第三方算子到VM工具箱的方法 4.3.1 联合开发&#xff1a;集成HALCON第三方算子到VM工具箱的方法 描述 环境&#xff1a;VM4.0及以上 VS2013 问题&#xff1a;有的用户在使用VisionMaster软件在开发视觉项目时&#xff0c;可能同时也…

来了来了,我使用 ChatGPT 开发了一个 AI 应用

ChatGpt 实在太火爆了&#xff0c;很多人在问我怎么使用 chatgpt 开发一个 AI 应用程序。这不就来了吗~ 开始 你所需要准备的一个OpenAI 的密钥和一点点代码来发送提示并返回结果&#xff0c;例如下面这段代码&#xff1a; import { OpenAIApi, Configuration } from openai…

基于RAM树莓派实现智能家居:语音识别控制,Socket网络控制,火灾报警检测,实时监控

目录 一 项目说明 ① 设计框架 ② 功能说明 ③ 硬件说明 ④ 软件说明 二 项目代码 <1> mainPro.c 主函数 <2> InputCommand.h 控制设备头文件 <3> contrlDevices.h 外接设备头文件 <4> bathroomLight.c 泳池灯 <5> livin…

GraphQL(三)DataLoader 详解

DataLoader是一个通用实用程序&#xff0c;用作应用程序数据获取层的一部分&#xff0c;通过批处理和缓存为各种远程数据源&#xff08;如数据库或 Web 服务&#xff09;提供简化且一致的 API 批处理 const user await userLoader.load(1); const invitedBy await userLoade…

【C++】7. auto和nullptr(c++11)

文章目录 一、auto二、nullptr 一、auto 在C98中&#xff0c;auto是一个存储类说明符&#xff0c;表示变量具有自动存储期&#xff0c;即在函数或块的作用域内创建和销毁。 在C11中&#xff0c;auto是一个类型占位符&#xff0c;表示变量的类型由其初始化器自动推断。 使用如下…

分类和扩展与继承

文章目录 [TOC](文章目录) 分类定义分类的使用使用场景使用注意点 Extension 扩展分类和扩展的区别 继承的定义使用注意点 新建一个分类 分类基础知识 分类 分类是指为已有的类添加方法&#xff0c;也可以说是将很多很复杂的代码划分为几个分区。 定义 分类的作用是扩展已有…

第十四届蓝桥杯大赛软件赛省赛 Java 大学 B 组题解

试题 A: 阶乘求和 本题总分&#xff1a;5 分 【问题描述】 令 S 1! 2! 3! ... 202320232023!&#xff0c;求 S 的末尾 9 位数字。 提示&#xff1a;答案首位不为 0。 【答案提交】 这是一道结果填空的题&#xff0c;你只需要算出结果后提交即可。本题的结果为一 个整数&am…

Linux信号:SIGCHLD信号和僵尸进程

1. SIGCHLD信号产生条件&#xff1a; &#xff08;1&#xff09;子进程终止&#xff1b; &#xff08;2&#xff09;子进程收到SIGSTOP暂停&#xff1b; &#xff08;3&#xff09;子进程处于暂停状态&#xff0c;收到SIGCONT被唤醒。 2. 捕捉SIGCHLD&#xff0c;避免僵尸进程&…

网络钓鱼:工作场所保护电子邮件安全的五个步骤

导语&#xff1a;Sophos电子邮件产品管理高级总监David Mitchell分享了他的主要技巧&#xff0c;以优化工作场所的电子邮件安全性。 Sophos电子邮件产品管理高级总监David Mitchell分享了他的主要技巧&#xff0c;以优化工作场所的电子邮件安全性。 尽管工作场所的聊天和即时…

云原生时代崛起的编程语言Go基础实战

文章目录 概述定义使用场景Go 安全 使用须知搜索工具Go基础命令标准库 基础语法Effective Go 概览命名规范注释变量常量(const)控制结构数据类型迭代&#xff08;range&#xff09;函数指针字符串和符文结构体(struct)方法接口(interface)泛型错误&#xff08;errors&#xff0…

iOS描述文件(.mobileprovision)一键申请

转载&#xff1a;IOS描述文件制作教程 iOS描述文件(.mobileprovision)一键申请 在主界面上点击描述文件按钮。 ​ 编辑切换为居中 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 新建ios描述文件 然后点击新建&#xff0c;然后输入描述文件名称&…

数据库设计与前端框架

数据库设计与前端框架 学习目标&#xff1a; 理解多租户的数据库设计方案 熟练使用PowerDesigner构建数据库模型理解前端工程的基本架构和执行流程 完成前端工程企业模块开发 多租户SaaS平台的数据库方案 多租户是什么 多租户技术&#xff08;Multi-TenancyTechnology&a…

【密码算法 之六】CCM 浅析

CCM模式浅析 1. 综述2. 加密2.1 前置条件&#xff08;Prerequisites&#xff09;2.2 输入&#xff08;Input&#xff09;2.3 输出&#xff08;Output&#xff09;2.4 加密流程&#xff08;Steps&#xff09; 3. 解密3.1 前置条件&#xff08;Prerequisites&#xff09;3.2 输入…

Orangepi Zero2 全志H616简介

为什么学 学习目标依然是Linux 系统 &#xff0c;平台是 ARM 架构 蜂巢快递柜&#xff0c;配送机器人&#xff0c;这些应用场景用C51,STM32单片机无法实现 第三方介入库的局限性&#xff0c;比如刷脸支付和公交车收费设备需要集成支付宝SDK&#xff0c;提供的libalipay.so 是…