C++初阶_2: inline内联函数 宏函数

news2024/11/24 5:20:37

C++推出了inline关键字,其目的是为了替代C语言中的宏函数。

我们先来回顾宏函数:

宏函数 

现有个需求:要求你写一个Add(x,y)的宏函数。

正确的写法有一种,错误的写法倒是五花八门,我们先来“见不贤而自省也。”

// 实现⼀个ADD宏函数的常⻅问题
#define ADD(int a, int b) return a + b;
#define ADD(a, b) a + b;
#define ADD(a, b) (a + b)

分析:

第一种写法:宏函数不是函数,它是宏,在预处理阶段就替换成了目标内容。

第二种写法:还是错的,少括号,较第一种好一点—— 在于没加分号

第三种写法:还是不对,不过好多了,错在少括号。

正确写法:

// 正确的宏实现
#define ADD(a, b) ((a) + (b))

这个写对了,不过“知其然,亦要知其所以然”。

// 为什么不能加分号 ?
// 为什么要加外⾯的括号 ?
// 为什么要加⾥⾯的括号 ?
//为什么不能添加分号 ;
#define Add(a,b) ((a) + (b));
int main()
{
    int ret1 = Add(1,2);//意在实现1 + 2并将结果赋值给ret1
    cout << ret1 << endl;//意在打印ret1
	return 0;
}

运行截图:

 

好戏还在后头:

cout << Add(1,3) << endl;

 

我们在前面曾说到:cout 能够自动识别变量类型,这里报错是因为函数的结果是它识别不了了嘛。 非也非也:

宏在预处理阶段,会将Add(1,3)替换成后面的表达式:这里若多加了分号,这行代码就在分号处中断了,而作为二元操作符的 << 前面无操作对象 后面又有个endl;自然因为缺少参数(表达式)报错。

cout << ((1) + (3)); << endl;//预处理阶段被替换成了如此模样

所以,宏函数里分号是多余的:

#define Add(a,b)  ((a) + (b))
int main()
{
    cout << Add(1,3) << endl;//这才能输出4
	return 0;
}

运行截图:

 

// 为什么要加外⾯的括号?
#define Add(a,b)  (a) + (b)
int main()
{
    cout << Add(1, 3) * 2 << endl;//意图输出 8 (4*2)
	return 0;
}
//替换后
//cout << Add(1, 3) * 2 << endl;
cout << (1) + (3) * 2 << endl;//然而据分析,输出7 (1+6)

 运行截图:

可见,外面括号是为了不改变运算表达式的结合的优先级,我们写个宏函数Add本意就是为了先算加法。

// 为什么要加⾥⾯的括号?
#define Add(a,b)  (a + b)
int main()
{
  int x = 1, y = 2;
  Add(x & y, x | y);//不加内括号,替换成 (x&y + x|y)
  return 0;
}
//但是忽略了加法作为算数运算符的优先级,其实结果会先算 y+x,故而与目标结果背道而驰

得出结论:宏函数不能加分号,为了避免保证运算中合理的优先级,应该在外面和里面加上括号。


C++表示:这宏函数是在预处理直接展开成了表达式,不像普通函数还要建立栈帧。但是也忒麻烦了点,稍有不慎,结果就不正确了。

于是,C++引入了关键字inline 就是替代C语言中的宏函数。

inline

inline既然要替代,首先它要延续宏函数的优点:调用函数时不用创建栈帧,直接展开,还要优于宏函数:方便书写—— inline 直接在函数定义的 返回类型前加上即可。

inline int Add(int x, int y)//现在写个函数+inline就方便很多了
{
	return x + y;
}
int main()
{
	int b = Add(1,2);
	return 0;
}
inline对于编译器⽽⾔只是⼀个建议,也就是说,你加了inline编译器也可以选择在调⽤的地⽅不展
开,不同编译器关于inline什么情况展开各不相同,因为C++标准没有规定这个。inline适⽤于频繁
调⽤的短⼩函数,对于递归函数,代码相对多⼀些的函数,加上inline也会被编译器忽略。
对于过长的函数作为内联函数,若也被频繁调用,此时已经不适合展开:比如下面这个场景:一个函数初次编译100条指令,编译完后成为一条指令。(在编译后的代码中,直接调用一个函数(尤其是在同一编译单元或模块内)通常涉及很小的开销,可能只是一条指令(比如一个跳转)。 )在函数在被复用时,就会执行被调用次的指令条数。

而如果编译器不阻拦这种展开,直接执行10000 * 100的指令,效率慢得可想而知。 

所以,inline适合短小、频繁调用的函数。

inline不建议声明和定义分离到两个⽂件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错。

比如:

//F.h 声明
#pragma once
inline void f(int a);
//F.cpp 函数定义
#include "F.h"
#include <iostream>
using namespace std;
void f(int a)
{
    cout << a << endl;
}
//执行文件
#include "F.h"
int main()
{
   f(10);
   return 0;
}

如上述代码所示:我们把声明和定义放在两个文件

报错:  

内联函数编译器默认认为是不需要地址的,因为已经在调用地方展开了。在测试代码中,头文件展开,但是只有函数F的声明,找不到它的实现,导致调用地方展不开函数。

为什么找不到它的实现(展不开函数),因为在F.cpp中,虽然也包含了F.h,但是前面加了inline,默认是内联函数。这时候就不会把函数的实现地址放进符号表,导致测试代码中,无法链接到所调用函数的定义。

等到后面有更多的知识补充,我们会更好理解链接错误这一概念

总结: inline不建议声明和定义分离到两个⽂件,分离会导致链接错误。

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

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

相关文章

我怎么会这么依赖 GUI?

AWS CLI、.NET 和 Lambda 函数 欢迎来到雲闪世界。在 Windows 上使用 Visual Studio 和 AWS Explorer 绝对会让你变得懒惰。我的意思是&#xff0c;能够通过右键单击项目来构建和部署 Lambda 函数之类的东西真是太棒了&#xff0c;但有时最好了解幕后发生了什么。 尽管如此&am…

Java - 异常

异常处理&#xff1a; ①捕获异常 选中代码后ctrlAltt: try catch捕捉异常 ②抛出异常 加上throws 异常类型 自定义异常 Exception.java: package Exception;//自定义运行时异常 public class ExceptionTest {public static void main(String[] args) {//保存一个合法的年…

知识库管理软件购买指南:2024年十大选择

本篇文章介绍了以下工具&#xff1a;PingCode、Worktile、蓝湖、语雀、幕布、Guru、Helpjuice、Stack Overflow for Teams、KnowledgeOwl、eXo Platform。 在企业中&#xff0c;信息分散、难以获取是个常见的痛点。无论是新员工入职、团队协作&#xff0c;还是项目管理&#xf…

电脑高手必备!这款数据恢复神器你值得拥有

哎呀&#xff0c;现在我们天天在用的手机、电脑和各种移动设备都是有很多日常和工作中的重要数据&#xff0c;丢失重要的文件和数据的时候就很着急&#xff0c;所以我也很感同身受&#xff0c;为此困扰过我好几次&#xff0c;所以今天特地借此文章整理了常用的失易得数据恢复软…

Unity游戏开发002

Unity游戏开发002 目录 第一章&#xff1a;Hello&#xff0c;Unity&#xff01;第二章&#xff1a;创建一个游戏体 本文目录 Unity游戏开发 Unity游戏开发002目录本文目录前言一、创建一个游戏体1. 编辑器语言设置2. 创建游戏对象的两种方法3. 快速复制和粘贴物体4. 注意事项…

/springmvc/xxx.html和/springmvc/xxx的区别

起因&#xff1a; 今天在访问webapp目录下的html文件时&#xff0c;突然报了500错误&#xff08;如下图&#xff09;&#xff0c;让我检查视图解析器&#xff0c;我寻思我访问的是静态资源&#xff0c;不是直接交给servlet处理嘛&#xff1f;需要什么视图解析器&#xff1f;&a…

13.C基础_预处理

预处理语句就是以#开头的语句。这些语句类型如下&#xff1a; #include&#xff1a;包含头文件#define&#xff1a;宏定义#undef&#xff1a;取消宏定义#ifdef&#xff0c;#endif&#xff1a;成对使用&#xff0c;判断是否定义了某个宏 宏定义 宏定义的本质就是原样替换&…

【Material-UI】Floating Action Button (FAB) 详解:动画效果 (Animation)

文章目录 一、FAB 按钮的动画概述1. 默认动画效果2. 多屏幕横向切换时的动画 二、FAB 动画效果的实现1. 代码示例&#xff1a;跨标签页的 FAB 动画2. 代码解析3. 多个 FAB 的切换 三、动画效果的最佳实践四、总结 在现代网页设计中&#xff0c;动画不仅提升了用户界面的动态感&…

React+AntDesign做一个日历,展示节假日,节气,并且在某几个时间上添加活动备注

直接贴效果图😄 首先日历是用的AntDesign提供的Calendar组件,这个组件还是蛮强大的,可以自定义头部时间下拉;渲染每个时间段,或者重置时间段内容,玩的空间是很大的 直接贴代码,结尾最后我会将开发中遇到的问题贴出来解答一下 第一步:下载js-calendar-converter添加…

SpringBoot集成日志框架

SpringBoot集成日志框架 Java生态体系日志框架介绍 简介 在Java生态体系中&#xff0c;围绕着日志&#xff0c;有很多成熟的解决方案。关于日志输出&#xff0c;主要有两类工具。 一类是日志框架&#xff08;Log4j、Logback&#xff09;&#xff0c;主要用来进行日志的输出的…

Unity 使用 NewtonSoft Json插件报错

JsonReaderException: Unexpected character encountered while parsing value: . Path , line 0, position 0. 通过断点发现&#xff0c;头有一串ZWNBSP&#xff0c;这个是BOM格式的JSON。在文件下看不到。 解决方法&#xff1a;改编码格式&#xff0c;Remove BOM.

Linux信号的概念信号的产生

前言 我们前面已经对进程已做了介绍&#xff01;知道进程具有独立性&#xff0c;但在运行起来后可能会"放飞自我"&#xff0c;即不受控制的执行&#xff0c;这就会导致系统崩溃等问题&#xff0c;非常不利于管理。因此OS需要一种机制来协调和控制进程的运行&#xf…

【C++】拓扑排序(BFS)

目录 拓扑排序介绍 有向无环图 如何解决这类问题 课程表 算法思路 代码实现 课程表2 算法思路 代码实现 火星词典 代码实现 拓扑排序介绍 有向无环图 入度&#xff1a;指向活动节点的箭头个数&#xff1b; 出度&#xff1a;从活动节点出去指向别的节点的箭头个数。…

交互式实时距离测量-单目测距-社交距离检测

使用说明 使用鼠标点击两个目标框要删除在距离计算过程中绘制的点&#xff0c;你可以使用鼠标右键点击。这会清除所有已绘制的点 使用 Ultralytics YOLOv8 进行距离计算 距离计算是在指定空间内测量两个物体之间间隙的基本概念。在 Ultralytics YOLOv8 的情况下&#xff0c;通…

React学习-初始化react项目

目标: reactv18&#xff1a;->1.核心的22中api2路由3.数据状态管理&#xff1a;redux项目&#xff1a; 1.b端业务闭环:登录方案、权限设计、用户管理方案、业务功能、系统架构设计、路由设计流程闭环&#xff1a;开发环境、生产环境、测试环境、代码规范、分支管理规范、项…

SpringBoot整合knife4j配置使用直接拷贝即可(快速入门超详细版)

1. SpringBoor整合Knife4j添加maven 1.1 第一种maven <!--添加Knife4j依赖--><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.5.0</ver…

Unity新输入系统 之 PlayerInput(真正的最后封装部分)

本文仅作笔记学习和分享&#xff0c;不用做任何商业用途 本文包括但不限于unity官方手册&#xff0c;unity唐老狮等教程知识&#xff0c;如有不足还请斧正​ 首先你应该了解新输入系统的基本单位和输入配置文件 Unity新输入系统 之 InputAction&#xff08;输入配置文件最基本的…

6 款最佳付费和免费 iPhone 解锁应用和软件

iPhone解锁应用程序是一种可以不受任何限制地移除 iOS 设备上不同类型锁的工具。iPhone 可能受锁屏密码、Apple ID 密码、屏幕使用时间密码、iCloud 激活锁、MDM 等保护。如果您忘记了密码&#xff0c;您将无法使用设备或无法完全访问您的 iPhone。幸运的是&#xff0c;有软件可…

跨平台控制神器Escrcpy,您的智能生活助手

Escrcpy 是一款基于 Scrcpy 开发的图形化安卓手机投屏控制软件&#xff0c;它允许用户将 Android 手机屏幕实时镜像到电脑上&#xff0c;并使用电脑的鼠标和键盘直接操作手机&#xff0c;实现了无线且高效的操控。这款软件是免费开源的&#xff0c;支持跨平台使用&#xff0c;包…

2024 年可免费下载的 6 款最佳 iOS 解锁软件

众所周知&#xff0c;如果所有者或其他人多次输入错误密码&#xff0c;iOS 会锁定并禁用 iPhone 或 iPad。Apple 推出了使用 iTunes/Finder、iCloud 或其他 iOS 设备解锁已禁用设备的方法。但是&#xff0c;每种方法都需要一些先决条件&#xff0c;例如 Apple 密码。在这种情况…