C++_2_ inline内联函数 宏函数(2/3)
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并将结果赋值给ret1cout << ret1 << endl;//意在打印ret1return 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;//这才能输出4return 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;
}

而如果编译器不阻拦这种展开,直接执行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不建议声明和定义分离到两个⽂件,分离会导致链接错误。
相关文章:

C++_2_ inline内联函数 宏函数(2/3)
C推出了inline关键字,其目的是为了替代C语言中的宏函数。 我们先来回顾宏函数: 宏函数 现有个需求:要求你写一个Add(x,y)的宏函数。 正确的写法有一种,错误的写法倒是五花八门,我们先来“见不贤而自省也。” // …...
ROS执行多个节点报错(遥控运动及SLAM建图)
今天在实体机器人中同时执行多个ROS节点: roslaunch rei_robot_base oryxbot_base.launchroslaunch robot_joy robot_joy.launchroslaunch oryxbot_slam oryxbot_slam_local.launch结果全部报错退出了 现在换一种执行方式: roscoreroslaunch rei_robot_base oryxbot_base.la…...
Spring Boot项目中实现文件的上传、下载和预览功能
在Spring Boot项目中实现文件的上传、下载和预览功能,可以通过使用Spring MVC的MultipartFile接口来处理文件上传,并使用HttpServletResponse或Resource来实现文件下载和预览。下面是如何实现这些功能的完整示例。 1. 引入依赖 确保在pom.xml中引入了S…...

【JAVA入门】Day21 - 时间类
【JAVA入门】Day21 - 时间类 文章目录 【JAVA入门】Day21 - 时间类一、JDK7前的时间相关类1.1 Date1.2 SimpleDateFormat1.3 Calendar 二、JDK8新增的时间相关类2.1 Date 相关类2.1.1 ZoneId 时区2.1.2 Instant 时间戳2.1.3 ZoneDateTime 带时区的时间 2.2 DateTimeFormat 相关…...

SQL server数据库备份和还原
新手小白都懂的sql server数据库备份和还原 一、备份 1.打开sql server数据库找到 2.展开找到对应的数据库文件 鼠标右击—任务–备份 3.复制名称 4.复制完点击添加 5.点击添加完之后再次点击查找路径 6.分别两个路径 原路径和新路径 (新路径是找到原路径新建了一…...

B站搜索建库架构优化实践
前言 搜索是B站的重要基础功能,需要对包括视频、评论、图文等海量的站内优质资源建立索引,处理来自用户每日数亿的检索请求。离线索引数据的正确、高效产出是搜索业务的基础。我们在这里分享搜索离线架构整体的改造实践:从周期长,…...

XSS反射实战
目录 1.XSS向量编码 2.xss靶场训练(easy) 2.1第一关 2.2第二关 方法一 方法二 2.3第三关 2.4第四关 2.5第五关 2.6第六关 2.7第七关 第一种方法: 第二种方法: 第三个方法: 2.8第八关 1.XSS向量编码 &…...
远程消息传递的艺术:NSDistantObject在Objective-C中的妙用
标题:远程消息传递的艺术:NSDistantObject在Objective-C中的妙用 引言 在Objective-C的丰富生态中,NSDistantObject扮演着至关重要的角色,特别是在处理分布式系统中的远程消息传递。它允许对象之间跨越不同地址空间进行通信&…...

指向派生类的基类指针、强转为 void* 再转为基类指针、此时调用虚函数会发生什么?
指向派生类的基类指针、强转为 void* 再转为基类指针、此时调用虚函数会发生什么? 1、无论指针类型怎么转,类对象内存没有发生任何变化,还是vfptr指向虚函数表,下面是成员变量,这在编译阶段就已经确定好了;…...

操作系统(Linux实战)-进程创建、同步与锁、通信、调度算法-学习笔记
1. 进程的基础概念 1.1 进程是什么? 定义: 进程是操作系统管理的一个程序实例。它包含程序代码及其当前活动的状态。每个进程有自己的内存地址空间,拥有独立的栈、堆、全局变量等。操作系统通过进程来分配资源(如 CPU 时间、内…...

react的setState中为什么不能用++?
背景: 在使用react的过程中产生了一些困惑,handleClick函数的功能是记录点击次数,handleClick函数被绑定到按钮中,每点击一次将通过this.state.counter将累计的点击次数显示在页面上 困惑: 为什么不能直接写prevStat…...

2.2算法的时间复杂度与空间复杂度——经典OJ
本博客的OJ标题均已插入超链接,点击可直接跳转~ 一、消失的数字 1、题目描述 数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗? 2、题目分析 (1)numsS…...

【CentOS 】DHCP 更改为静态 IP 地址并且遇到无法联网
文章目录 引言解决方式标题1. **编辑网络配置文件**:标题2. **确保配置文件包含以下内容**:特别注意 标题3. **重启网络服务**:标题4. **检查配置是否生效**:标题5. **测试网络连接**:标题6. **检查路由表**࿱…...

Linux 操作系统 --- 信号
序言 在本篇内容中,将为大家介绍在操作系统中的一个重要的机制 — 信号。大家可能感到疑惑,好像我在使用 Linux 的过程中并没有接触过信号,这是啥呀?其实我们经常遇到过,当我们运行的进程当进程尝试访问非法内存地址时…...

黑马前端——days09_css
案例 1 页面框架文件 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-UA-Compati…...
【Python爬虫】技术深度探索与实践
目录 引言 第一部分:Python爬虫基础 1.1 网络基础 1.2 Python爬虫基本流程 第二部分:进阶技术 2.1 动态网页抓取 2.2 异步编程与并发 2.3 反爬虫机制与应对 第三部分:实践案例 第四部分:法律与道德考量 第五部分&#x…...

智启万象|挖掘广告变现潜力,保障支付安全便捷
谷歌致力于为开发者提供 先进的广告变现与支付解决方案 一起回顾 2024 Google 开发者大会 了解如何利用谷歌最新工具和功能 提高变现收入,优化用户体验,保障交易安全 让变现更上一层楼 广告检查器是谷歌 AdMob 平台最新推出的高级测试工具,开…...

函数递归,匿名、内置行数,模块和包,开发规范
一、递归与二分法 一)递归 1、递归调用的定义 递归调用:在调用一个函数的过程中,直接或间接地调用了函数本身 2、递归分为两类:直接与间接 #直接 def func():print(from func)func()func() # 间接 def foo():print(from foo)bar…...

Springboot3 整合swagger
一、pom.xml <dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-api</artifactId><version>2.1.0</version></dependency> 二、application.yml # SpringDoc配置 # springdoc:swa…...
查看同一网段内所有设备的ip
使用命令提示符(CMD)进行扫描 查看本机IP地址 首先通过 ipconfig /all 命令查看本机的IP地址,确定你的网段,例如 192.168.1.。 Ping网段内每个IP地址 接着使用循环命令: for /L %i IN (1,1,254) DO ping -w 1 -n …...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...

【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...