【C++入门必备知识:内联函数+指针空值nullptr】

news2024/11/17 2:56:38

【C++入门必备知识:内联函数+指针空值nullptr】

  • ①.内联函数
    • Ⅰ.概念
    • Ⅱ.宏与内联
    • Ⅲ.总结
  • ②.指针空值nullptr(C++11)
    • Ⅰ.C++98中的指针空值
    • Ⅱ.注意:

在这里插入图片描述

①.内联函数

Ⅰ.概念

用inline修饰的函数就叫做内联函数,编译时C++编译器会在调用内联函数的地方将函数直接展开,而不是去调用函数,没有建立函数栈帧的开销,跟C语言的宏的方式类似,内联函数提高了程序运行的效率。

using namespace std;
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int ret = 0;
	ret = Add(2, 3);
	return 0;
}

我们知道调用一个函数需要给这个函数建立函数栈帧。
当我们需要调用很多次该函数时,就需要花费大量的空间。
在C语言中我们是使用宏定义一个函数来解决这样的问题的。

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

我们要是有宏定义上面的函数该如何定义呢?
正确的写法应该是这样:

#define Add(x,y) (((x)+(y))

注意这里最最后没有分号。
当我们使用宏函数时,可以避免调用函数时建立栈帧的开销,因为当编译时,编译器会将宏函数直接展开,不会去调用开辟栈帧。
在这里插入图片描述
不过定义宏函数有很多易错点:
1.不加括号会引起很多错误

1.#define Add(x,y) x+y 
当没有括号时
Add(1,2)*5;
因为Add是直接替换
所以结果就变成了1+2*5
2.#define Add(x,y) (x+y)
这种场景虽然外面加了括号,但里面没有仍然会出错
比如这样的场景,当x,y为表达式时呢?
你如何确定是+的优先级高还是x和y里面表达式的运算符优先级高呢?
比如Add(1|2,3&4);
直接替换为1|2+3&4,这里+号的优先级是比|&高的。

2.注意宏函数定义,最后没有分号。

Ⅱ.宏与内联

优点:
1.可以增强代码的复用性

2.可以提高效率
缺点:
1.不方便调试,因为编译期间进行了替换

2.代码复杂,可读性差,容易误读

3.没有类型检查,安全性差。

虽然宏具有优势,但是它的缺点也是很多的,所以在C++中改变这样方法,引入了内联函数的概念。

我们只要在函数的前面加上关键字inline就变成了内联函数了。
而且内联函数既可以提高效率,可读性也好。

接下来让我们看看内联函数到底是如何使用的。

using namespace std;
inline int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int ret = 0;
	ret = Add(2, 3);
	return 0;
}

如何查看?
1.在release模式下,查看编译器生成的汇编代码中是否存在call Add,但在release版本下,不支持调试。
2.在debug版本下,因为编译器系统原因无法查看内联函数展开,所以我们需要对编译器进行一些调整
在这里插入图片描述

对于内联函数来说,函数在编译时期直接展开了。
在这里插入图片描述
如果不是内联函数就需要调用函数,建立函数栈帧。

using namespace std;
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int ret = 0;
	ret = Add(2, 3);
	return 0;
}

在这里插入图片描述
那内联函数这么好用,那以后是个函数都搞成内联函数可以吗?
当然不可以,内联函数和宏函数都是有使用范围的。
都适合于那些短小的频繁调用的函数,如果不是这样的函数,那么可能会造成代码膨胀的。
在这里插入图片描述
在这里插入图片描述
而当函数A代码量很时,又是内联函数,结果会使项目B中生成的可执行程序变大,使代码膨胀。所以长的函数不适合作为内联函数。

但其实编译器设计者也注意到这点,所以在设计时就规定,inline对于编译器仅仅只是一个建议,最终是否会成为内联函数,编译器自己决定,也就是编译器不相信你,大千世界无奇不有,谁知道哪个程序员整幺子然后还要编译器背锅呢。
所以像比较长的函数,递归函数类似的函数就算加了inline,编译器也会将之否决掉。

还有定义内联函数时,声明和定义不能分开定义。不然会存在链接问题。
在这里插入图片描述
因为在编译期间内联函数就已经展开,哪里函数没有地址,也不会进入符号表,但是当使用函数时,只要函数的声明,没有函数的地址,那么就要去定义里去找函数的地址,但遗憾的是,内联函数已经展开,所以没有地址存在,所以最终是无法编译通过的。
在这里插入图片描述
所以正确的写法是,定义和声明不分开,也就是直接在.h文件里将函数的声明和定义一起写即可。
在这里插入图片描述
所有要用该函数的地方,直接在头文件里声明定义就可以了。这样就不存在链接的问题。

Ⅲ.总结

- 1.inline是一种以空间换时间的做法,如果编译器将函数当作内联函数处理,那么在编译阶段,会用函数体替换函数调用。
缺陷:目标文件会变大。
优势:减少调用开销,提高程序运行效率

- 2.inline 对于编译器只是一个建议,不同编译器关于inline实现机制不同,一般对于函数规模较小的(即函数不是长的),不是递归,并且是频繁调用的函数采取inline修饰,否则编译器会忽略inline的特性。
在C++prime中也说过:

内联函数只是向编译器发出的一个请求,编译器可以选择忽略这个请求。

3.inline内联函数不能声明和定义分离,分离会导致链接问题,因为inline分离,内联函数在编译时就展开,没有函数地址,而声明又没有地址,在链接时找不到定义里的地址。

②.指针空值nullptr(C++11)

Ⅰ.C++98中的指针空值

在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,那么我们是用NULL来对其初始化的。

int *a =NULL;
//指针不知道指向

NULL实际上是一个宏,在传统的C头文件(stddef.h)中是这样定义的:

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

可以看到,NULL可能被定义为字面常量0,或者被定义为无类线指针(void*)的常量。
但是不论是哪种定义,在C++中使用空值的指针时,都会不可避免的遇到一些问题。

void f(int)
{
	cout << "f(int)" << endl;
}
void f(int*)
{
	cout << "f(int*)" << endl;
}

两个同名函数构成重载,一个参数是int 类型,一个参数是int*类型
如果我们调用这两个函数该如何调用呢?

int main()
{
	f(0);//调用第一个
	f(NULL);//这个调用第几个函数呢?
	return 0;
}

在这里插入图片描述
从结果我们可以知道,f(0)和f(NULL)都调用的是第一个函数,可是我们本意是向f(NULL)调用第二个函数的。
因为NULL定义就是字母常量0或者无类型的(void*)常量,所以它会调用第一个函数,而第二个函数的参数是int *类型的。

在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针常量,但是编译器默认情况下将其看成一个整形常量,如果要将其按照指针方式使用,必须要对其进行强制转化由NULL强制转化为int*类型

f((int*)NULL);

这样才可以调用第二个函数。
但在C++中引入nullptr关键字,表示指针空值。
所以我们直接这样写f(nullptr)就可以调用第二个函数

void f(int)
{
	cout << "f(int)" << endl;
}
void f(int*)
{
	cout << "f(int*)" << endl;
}
int main()
{
	f(0);
	f(NULL);
	f((int*)NULL);
	f(nullptr);
	return 0;
}

在这里插入图片描述

Ⅱ.注意:

1.在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。

2.在C++11中,sizeof(nullptr)与sizeof((void)0)所占的字节数相同。

3.为了提高代码的健壮性,在代码中最好使用nullptr表示指针空值。

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

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

相关文章

密歇根大学,一个被低估的美国公立常春藤名校

密歇根大学&#xff08;University of Michigan&#xff09;创建于1817年&#xff0c;是美国历史最悠久的公立大学之一&#xff0c;被誉为“公立常春藤”和“公立大学的典范”&#xff0c;与加州大学伯克利分校和威斯康星大学麦迪逊分校等大学一起代表了美国公立大学的最高水平…

Unity Camera -- (1)概览

Camera章节笔记所用的资源包在这里&#xff1a; https://connect-prd-cdn.unity.com/20230208/a0898204-bc36-4d6e-a3b2-d4b83ae67c1d/CreativeCore_Camera_2021.3LTS.ziphttps://connect-prd-cdn.unity.com/20230208/a0898204-bc36-4d6e-a3b2-d4b83ae67c1d/CreativeCore_Came…

ERTEC200P-2 PROFINET设备完全开发手册(9-1)

9. PROFIDRIVE AC1/AC4参考代码 PROFIdrive是西门子 Profibus 与 Profinet 两种通讯方式针对驱动的生产与自动化控制应用的一种协议框架&#xff0c;也可以称作“行规”&#xff0c; PROFIdrive使得用户更快捷方便实现对驱动的控制。PROFIdrive的最大特点是互操作性 – 不同厂…

低代码平台名声臭,用起来却真香——60%开发者不敢承认

群体盲从意识会淹没个体的理性&#xff0c;个体一旦将自己归入该群体&#xff0c;其原本独立的理性就会被群体的无知疯狂所淹没。——《乌合之众》 不知道从什么时候开始&#xff0c;“低代码不行”的论调充斥着整个互联网圈子&#xff0c;csdn、掘金、知乎、B站、脉脉……到处…

遗传算法求取函数最值问题

目录 1. 关于遗传算法 2. 遗传算法的步骤 3. 代码实现 3.1 工具函数 3.1.1 目标函数 3.1.2 解码 3.1.3 交叉 3.1.4 变异 3.2 主函数部分 3.3 代码 4. 其他 1. 关于遗传算法 遗传算法是根据生物进化论提出的计算最优解的一种算法&#xff0c;核心思想是物竞天择&…

九龙证券|光模块概念股封单资金超3亿元,传媒板块涨停潮来袭

今天A股三大股指低开低走。沪深两市收盘共37股涨停。剔除4只ST股&#xff0c;合计33股涨停。另外&#xff0c;10股封板未遂&#xff0c;整体封板率为78.72%。 涨停战场&#xff1a; 华工科技封单资金超3亿元 从收盘涨停板封单量来看&#xff0c;同方股份封单量最高&#xff0…

【UE】制作简单的山脉地形

在上一篇博客中&#xff08;【UE】使用Quixel Bridge下载免费贴图&#xff09;&#xff0c;介绍了如何下载免费贴图&#xff0c;本篇博客介绍如何使用这些贴图制作地形贴图。 1. 创建地形 2. 用雕刻工具绘制地形 3. 新建两个材质函数&#xff0c;分别命名为“GrassAuto”、“R…

UWERANSIM - OAI5GC分立部署教程

环境&#xff1a; Ubantu18.04OAI-5GCv1.5.0UERANSIMv3.2.6 网络&#xff1a; Host1&#xff1a;OAI-5GCens37&#xff1a;192.168.12.3Host2&#xff1a;UERANSIMens40&#xff1a;192.168.12.33 确保两台宿主机之间互通&#xff01; 网络配置 Host1 网络&#xff1a;OA…

掌握 Web3 游戏数据分析,详述 4 个开发者需追踪的关键指标

引入&#xff1a;需要关注的关键指标包括哪些 区块链游戏在开发运营过程中需要追踪的关键指标包括红馆加密市场数据&#xff0c;DAU、MAU 和用户留存相关的用户数据、社交媒体参与数据&#xff0c;以及游戏内资产等生态系统相关数据。 主要观点&#xff1a; GameFi 项目与传统…

我国集成电路行业发展重心逐步转向芯片设计 高端设备材料领域国产化迫在眉睫

1、集成电路概念及其产业链图解 集成电路&#xff08;integrated circuit&#xff09;缩写为IC&#xff0c;是一种微型电子器件或部件&#xff0c;其是采用一定工艺将一块电路所需的晶体管、二极管、电阻、电容和电感等电子元器件制做在一块或几小块晶片或晶片上&#xff0c;然…

Navicat连接oracle数据库时报ORA-28547错的解决方法(亲测有效)

​ 文章目录 一. 问题说明二. 问题解决1. 查询Oracl版本2. 下载Oracle对应oci.dll文件3. 修改oci配置4. 重启Navicat并连接Oracle5. 总结navicat配置oci教程步骤 一. 问题说明 这是因为Navicat自带的oci.dll并不支持oracle11g&#xff0c;需要去官网下载对应支持的版本。 二.…

WIFI6模块AP6275系列选性参考和外围应用电路参考

AP6275系列是采用28纳米工艺的BCM43752方案设计、封装15x13mm/LGA-50&#xff0c;有SDIO接口和PCIe接口可选&#xff0c;同时还有蓝牙和WiFi天线是否共用区分&#xff1b;具体如下&#xff1a; AP6275S通信接口SDIO3.0WiFi/UARTBT5.3&#xff1b;吞吐量TX>450Mbps、RX>55…

Raft 共识算法1-Raft基础

Raft 共识算法1-Raft基础 Raft算法中译版地址&#xff1a;http://www.redisant.cn/etcd/contact 英原论文地址&#xff1a;https://raft.github.io/raft.pdf Etcd Assistant 是一款 etcd 可视化管理软件&#xff0c;便捷高效地操作您的 etcd 集群&#xff1b;支持多种键的视图&…

【Cisco Packet Tracer| 三.单交换机划分VLAN】

文章目录 一.实验原理二.连接实验拓扑图1.给主机和交换机之间连线2.给四台主机设置IP地址 三.未划分VLAN情况下查看4台主机间是否能ping通四.创建并划分VLAN1.创建VLAN2.划分VLAN 五.划分VLAN后查看4台主机间是否能ping通 一.实验原理 原理&#xff1a;单交换机连接4台主机&…

医药之家:国内首个三价轮状病毒疫苗上市!预防轮状病毒导致的婴幼儿腹泻

医药之家获悉&#xff0c;4月17日&#xff0c;国药集团中国生物兰州生物制品研究所自主研发的口服三价轮状病毒减毒活疫苗&#xff08;Vero细胞&#xff09;获批上市&#xff0c;这也是国内首个获准上市的三价轮状病毒疫苗。 轮状病毒是一种双链RNA病毒&#xff0c;常见于6个月…

若依系统部署在linux系统 验证码报错:FontConfiguration.getVersion报空指针异常

最近遇到一个问题&#xff1a;若依前后端分离后端服务部署到linnux系统后访问/capthaImage 报空指针异常&#xff01; 报错如下&#xff1a; 首先看问题sun.awt.FontConfiguration.getVersion(); 这是jdk的问题啊&#xff01; 首先查看linux系统的jdk信息 openjdk version &q…

数据库基础篇 《6. 多表查询》

目录 1. 一个案例引发的多表连接 1.1 案例说明 1.2 笛卡尔积&#xff08;或交叉连接&#xff09;的理解 1.3 案例分析与问题解决 2. 多表查询分类讲解 分类1&#xff1a;等值连接 vs 非等值连接 等值连接 非等值连接 ​编辑 分类2&#xff1a;自连接 vs 非自连接 分类3&…

C语言从入门到精通第7天(scanf、printf、getchar函数的使用)

scanf、printf、getchar函数的使用 printf函数scanf函数getchar函数 printf函数 在C语言中使用printf函数进行格式化的输出&#xff0c;它是一个可变的参数函数&#xff0c;参数的个数不定。在前面我们已经学习了各个类型的打印格式&#xff0c;这里就对最常见的输出格式进行介…

如何在模拟器里面脱360的壳

1.准备环境 1.夜神模拟器 2.pyhton3.8 3.frida的版本 16.0.17 4.frida-dexdump 2.设置adb连接 我们打开夜神模拟器所在的文件夹&#xff0c;里面有自带的adb&#xff0c;我们在这个文件夹里面打开cmd。在里面链接上夜神模拟器。 adb devices 我这边显示链接成功了。 3.…

全平台数据(数据库)管理工具 DataCap 管理 Rainbond 上的所有数据库

DataCap是用于数据转换、集成和可视化的集成软件&#xff0c;支持多种数据源、文件类型、大数据相关数据库、关系数据库、NoSQL数据库等。通过该 DataCap 可以实现对多个数据源的管理&#xff0c;对数据源下的数据进行各种操作转换&#xff0c;制作数据图表&#xff0c;监控数据…