【C++初阶(三)】引用内联函数auto关键字

目录

 前言

 1. 引用

  1.1 引用的概念 

 1.2 引用的特性

 1.3 引用的权限

 1.4 引用的使用

 1.5 引用与指针的区别

2. 内联函数

2.1  什么是内联函数

2.2  内联函数的特性

 3. auto关键字

 3.1 auto简介

 3.2 auto使用规则

 3.3 auto不能使用的场景

4.  基于范围的for循环

 4.1 范围for使用

 4.2 使用条件

5. C++空指针 

总结


 前言

        在学习C语言时,大家或许都被指针为难过,在使用指针时也存在各种问题,比如:空指针野指针问题(指针可以在任何时候指向任何地址,包括无效地址)。此外在C语言中函数调用时,如果多次的调用同一函数,创建大量的函数栈帧就会导致性能下降,对于这些缺点,C++都进行了优化与改进。那么本期的 “主角” 就是引用&内联函数。

在这里插入图片描述

 1. 引用

  1.1 引用的概念 

         引用是C++中的一个重要概念,它是一个已存在变量的别名引用不是新定义一个变量,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。对引用的操作实际上就是对原变量的操作。

引用的定义方式为:类型& 引用名 = 原变量名; 

 比如:

int main()
{
	int a = 1;
	int& b = a;
	a++;
	b++;//b++也就是a++

	cout << a << endl;//输出3
	return 0;
}

注意:引用类型必须和引用实体是同种类型的。

 1.2 引用的特性

  • 引用必须初始化
  • 不占用额外空间(它引用的变量共用同一块内存空间)
  • 不能修改引用的绑定(引用一旦引用一个实体,就不能再引用其他实体)
  • 引用可作为函数参数和返回值 

int main()
{
	int a = 1;
	int b = a;
	
	//int& t;出现报错

	int& c = a;
	int& d = a;
	int& e = c;

	//地址相同
	cout << &a << endl;
	cout << &c << endl;
	cout << &d << endl;
	cout << &e << endl;
	return 0;
}

 1.3 引用的权限

         在C++中,引用的权限与指针类似,可以分为两种权限:常量引用和非常量引用。

         常量引用:使用const修饰的引用被称为常量引用。常量引用只能读取被引用变量的值,不能修改被引用变量的值。

        如果引用实体使用const修饰,那引用也必须使用const修饰。也就是权限可以被缩小,但不可以被放大。

比如:

int main()
{
	const int a = 1;
	//int& b = a;出现报错,权限被放大
	    
     //权限要等大
    const int a = 1;
    const int& b = a;

    //权限可以缩小
    int c = 2;
    const int& d = c;

	return 0;
}

除此之外,在类型转换时创建的临时变量也具有常属性。

int main()
{
	
	int i = 1;
	double j = i;

	//double& rj = i;//报错
	//类型转换过程中
	//存在隐式转换产生临时变量,这里的临时变量具有常性
	const double& rj = i;	

	return 0;
}

 1.4 引用的使用

    做参数

void Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}

 和指针相比

void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

 使用上更加简洁,并且引用的传值效率也很高,比如我们传值时传一个较大的数组,传值调用将一个很大内存的数组转换为数组地址传过去(变为4字节)

struct A{ int a[10000]; };
void Func1(A a){}
void Func2(A& a){}

 这些指针可以完成的工作,引用也可以完成。

   做返回值

int& Add(int a, int b)
{
	static int c = a + b;
	return c;
}

在之前我们先来理解一下传值返回:

int Count()
{
	int n = 0;
	n++;

	return n;
}

int main()
{
	int ret = Count();

	cout << ret << endl;
    return 0;
}

    这里返回的n是Count函数里的n吗?答案不是,在C语言函数中,函数执行结束函数内创建的变量就会销毁,所以这里返回的是Count()函数中变量n数值的拷贝(返回的是n的值(临时变量),而不是n这个变量)。

int& Count()
{
	int n = 0;
	n++;

	return n;
}

      如果使用引用做返回值,返回的就是n的别名,也可以理解为返回的就是n这个变量(会报警告:返回局部变量或临时变量的地址: n)。这样直接返回函数内变量是很危险的,因为函数内的n出了函数作用域就被销毁了,再返回原本的n地址处的数据,此时数据是不可控的。

int main()
{
	int& ret = Count();

	cout << ret << endl;//第一次输出还是正常值1

	cout << ret << endl;//第二次输出就变成随机值了
}

 1.5 引用与指针的区别

 在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间

int main()
{
	int a = 10;
	int& ra = a;
	cout << "&a = " << &a << endl;//输出的地址相同
	cout << "&ra = " << &ra << endl;
	return 0;
}

 在底层实现上实际是有空间的,因为引用是按照指针方式来实现的

 下边是引用与指针底层汇编的对比:

引用与指针的不同点:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  4. 没有NULL引用,但有NULL指针
  5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
    位平台下占4个字节)
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  7. 有多级指针,但是没有多级引用
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  9. 引用比指针使用起来相对更安全

2. 内联函数

2.1  什么是内联函数

      以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。

 如上图,函数在调用时并没有创建函数栈帧,而是直接在main函数内部展开。

2.2  内联函数的特性

  • inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用

缺陷:可能会使目标文件变大,

优势:少了调用开销,提高程序运行效率.

 但也并不是所有的函数都要展开,内联函数展开的也只适用于较小的函数。

比如:

        一个函数有100行代码,被调用了1000次,每次都展开,那就是10w行代码,产生的文件要多大,而如果是函数栈帧调用,每次调用都去同一块空间调用函数,也就只多了100行代码。

  •  inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性

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

  •  声明和定义分离时不可以使用内联函数

 分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到

内联函数在声明和定义分离时,直接调用会出现报错,但是可以间接调用。

//fun.h
inline void fun()
{
	cout << "hello ,world!" << endl;
}

//fun.c
void func()
{
	fun();
}

//test.c
int main()
{
	fun();
	return 0;
}

 这种情况是可以正常调用的。

 3. auto关键字

 随着程序越来越复杂,程序中用到的类型也越来越复杂,经常体现在:

  • 类型难于拼写
  • 含义不明确导致容易出错

 3.1 auto简介

        使用auto修饰的变量,是具有自动存储器的局部变量 ,但是在日常中却很少使用。

        C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

 比如:

int Test()
{
	return 10;
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto d = Test();
	cout << typeid(b).name() << endl;//int
	cout << typeid(c).name() << endl;//char
	cout << typeid(d).name() << endl;//int
	
	return 0;
}

auto会自动读取类型,并且使用auto定义变量时必须对其进行初始化

注意:

        使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译器会将auto替换为变量实际的类型

 3.2 auto使用规则

  •  与指针引用结合使用

 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

int main()
{
	int x = 10;
	auto a = &x;
	auto* b = &x;
	auto& c = x;
	cout << typeid(a).name() << endl;//int*
	cout << typeid(b).name() << endl;//int*
	cout << typeid(c).name() << endl;//int

	return 0;
}
  • 一行定义多个变量

        当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译
器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

auto a = 1, b = 2;
auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化类型不同

 3.3 auto不能使用的场景

  •  auto不能作为函数返回类型

最新的C++语法是可以的,但是很不推荐,在日常应用场景中一定不要这样写,在较大的项目中大多数都是多人合作的,如果你使用auto,那别人在调用你的函数接口时不知道返回类型很难搞,如果别人也用auto,这样就会导致一系列连锁,最后代码越写越 “ 屎山 ”,这就是典型的 “ 坑队友 ”,要养成良好的代码风格。

  •  auto不能作为函数的参数
void TestAuto(auto a)//出现报错,编译器无法对a的实际类型进行推导
{

}
int main()
{
	TestAuto(2);
}
  • auto不能直接声明数组

 

4.  基于范围的for循环

 4.1 范围for使用

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
    //正常的for循环遍历
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		cout << arr[i] << ' ';
	}
	cout << endl;
    //范围for遍历
	for (int e : arr)
	{
		cout << e << ' ';//这里的e是数组值的拷贝,改变e无法改变数组的数据
	}
	return 0;
}

         对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。

 注意:范围for与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环。

 4.2 使用条件

  • for循环迭代的范围必须是确定的 
void TestFor(int array[])
{
  for(auto& e : array)
    cout<< e <<endl;
}

        例如上述代码,范围并不明确。对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围

5. C++空指针 

        在C语言中我们常用的都是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 main()
{
	f(0);			//f(int)
	f(NULL);		//f(int)
	f((int*)NULL);	//f(int*)
	return 0;
}

 不难发现NULL在C++中被替换成了0,在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void*)0。

为了保险起见,C++引入nullptr,代表空指针,在后续表示指针空值时建议最好使用nullptr。


总结

        到本期C++的一些入门简单语法已经基本介绍完毕,后续将会继续深入学习C++,以上便是本期全部内容 ,最后,感谢阅读!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mathtype怎么更改编号 mathtype章节编号错乱怎么办

mathtype作为一款功能强大的公式编辑器&#xff0c;使用范围广泛&#xff0c;与多款软件兼容。但新手可能会对mathtype的操作不熟悉&#xff0c;不知道如何在mathtype中更改编号&#xff0c;以及解决章节编号错乱问题。本文将围绕mathtype怎么更改编号&#xff0c;mathtype章节…

toon boom harmony基础

以下都是tbh快捷键使用&#xff0c;或者一些常用功能介绍 1、在节点视图中&#xff0c;按回车可直接弹出节点库搜索框 2、中心线编辑器 只能编辑用笔刷画出来的线条&#xff0c;铅笔画出来的线条无法编辑。 3、镜头标记 1 右键箭头方向&#xff0c;可弹出下拉&#xff0c;&am…

StripedFly恶意软件框架感染了100万台Windows和Linux主机

导语 近日&#xff0c;一款名为StripedFly的恶意软件框架在网络安全研究人员的监视之外悄然感染了超过100万台Windows和Linux系统。这款跨平台的恶意软件平台在过去的五年中一直未被察觉。在去年&#xff0c;卡巴斯基实验室发现了这个恶意框架的真实本质&#xff0c;并发现其活…

【C++的OpenCV】第十四课-OpenCV基础强化(二):访问单通道Mat中的值

&#x1f389;&#x1f389;&#x1f389; 欢迎各位来到小白 p i a o 的学习空间&#xff01; \color{red}{欢迎各位来到小白piao的学习空间&#xff01;} 欢迎各位来到小白piao的学习空间&#xff01;&#x1f389;&#x1f389;&#x1f389; &#x1f496;&#x1f496;&…

现代挖掘机vr在线互动展示厅是实现业务增长的加速度

VR数字博物馆全景展示充分应用5G、VR全景、web3d开发和三维动画等技术&#xff0c;将实体博物馆整体还原到3D数字空间&#xff0c;让游客360全景漫游式参观&#xff0c;无论大小、贵重、破损的典藏展品都能通过3D建模技术&#xff0c;逼真重现到三维虚拟场景中&#xff0c;让参…

在全新ubuntu上用gpu训练paddleocr模型遇到的坑与解决办法

目录 一. 我的ubuntu版本![在这里插入图片描述](https://img-blog.csdnimg.cn/297945917309494ab03b50764e6fb775.png)二.首先拉取paddleocr源代码三.下载模型四.训练前的准备1.在源代码文件夹里创造一个自己放东西的文件2.准备数据2.1数据标注2.2数据划分 3.改写yml配置文件4.…

OS的Alarm定时器调度机制

调度表触发的任务在编译时就被静态定义&#xff0c;任务的触发时间和执行顺序是固定的。这种方式适用于已知的、固定的任务触发模式&#xff0c;例如周期性任务或事件驱动任务。而使用 Alarm 机制触发的任务具有更大的灵活性。Alarm 允许在运行时动态地设置和修改任务的触发时间…

Megatron-LM GPT 源码分析(三) Pipeline Parallel分析

引言 本文接着上一篇【Megatron-LM GPT 源码分析&#xff08;二&#xff09; Sequence Parallel分析】&#xff0c;基于开源代码 GitHub - NVIDIA/Megatron-LM: Ongoing research training transformer models at scale &#xff0c;通过GPT的模型运行示例&#xff0c;从三个维…

论文阅读——GPT3

来自论文&#xff1a;Language Models are Few-Shot Learners Arxiv&#xff1a;https://arxiv.org/abs/2005.14165v2 记录下一些概念等。&#xff0c;没有太多细节。 预训练LM尽管任务无关&#xff0c;但是要达到好的效果仍然需要在特定数据集或任务上微调。因此需要消除这个…

基于边缘智能网关的储能系统安全监测管理方案

“储能系统充电”是配套新能源汽车产业发展的重要应用之一。得益于电池技术的发展&#xff0c;新能源汽车正逐步迈入快充时代&#xff0c;由于在使用快速充电桩时&#xff0c;可能导致用电峰值负荷超过电网的承载能力&#xff0c;对于电网的稳定性和持续性会有较大影响&#xf…

nodejs+vue+elementui+express酒店管理系统

登录&#xff1a;运行系统后&#xff0c;进行登录&#xff0c;可使用本系统。 客房预定&#xff1a;此界面先通过条件查询客房信息&#xff0c;然后进行客房预定。对预定的客房还可以取消和支付操作。 信息查询&#xff1a;可查询所有的公告信息&#xff0c;点击公告名称&#…

单目3D目标检测 方法综述——直接回归方法、基于深度信息方法、基于点云信息方法

本文综合整理单目3D目标检测的方法模型&#xff0c;包括&#xff1a;基于几何约束的直接回归方法&#xff0c;基于深度信息的方法&#xff0c;基于点云信息的方法。万字长文&#xff0c;慢慢阅读~ 直接回归方法 涉及到模型包括&#xff1a;MonoCon、MonoDLE、MonoFlex、CUPNet…

Unity的碰撞检测(五)

温馨提示&#xff1a;本文基于前一篇“Unity的碰撞检测(四)​​​​​​​”继续探讨两个游戏对象具备刚体的BodyType均为Dynamic&#xff0c;但是Collision Detection属性不同的碰撞检测&#xff0c;阅读本文则默认已阅读前文。 &#xff08;一&#xff09;测试说明 在基于两…

Git窗口打开vim后如何退出编辑(IDEA/Goland等编辑器)

最近在学习git高级操作过程中&#xff0c;遇到了一下问题&#xff1a; 我在学习Git合并多个commit为一个的时候&#xff0c;需要输入一个命令 git rebase -i HEAD~2 这说明已经是编辑模式了。当我写好后&#xff0c;我还按照原来在linux上的按下ESC键&#xff0c;但是只是光…

【错误解决方案】ModuleNotFoundError: No module named ‘cPickle‘

1. 错误提示 在python程序中试图导入一个名为cPickle的模块&#xff0c;但Python提示找不到这个模块。 错误提示&#xff1a;ModuleNotFoundError: No module named cPickle 2. 解决方案 实际上&#xff0c;cPickle是Python的pickle模块的一个C语言实现&#xff0c;通常用于…

分类预测 | MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双向门控循环单元数据分类预测

分类预测 | MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双向门控循环单元数据分类预测 目录 分类预测 | MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双向门控循环单元数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.MATLAB实现SSA-CNN-BiGRU麻雀算法优化卷积双…

2023年中国机场建设标准、机场数量及机场系统投资完成情况分析[图]

机场&#xff0c;亦称飞机场、空港&#xff0c;较正式的名称是航空站。机场有不同的大小&#xff0c;除了跑道之外&#xff0c;机场通常还设有塔台、停机坪、航空客运站、维修厂等设施&#xff0c;并提供机场管制服务、空中交通管制等其他服务。 机场建设资质等级标准 资料来源…

2-多媒体数据压缩国际标准

文章目录 多媒体数据压缩编码的重要性和分类为什么要压缩?计算: 未压缩音频的数据率简答: 环绕声系统-作业题9(简述7.4.3全景声)计算: 未压缩图像的数据量-作业题10(估计尺寸及容量)计算: 未压缩视频的数据率 为什么能压缩?数据压缩编码的两大类无损压缩算法: LZ77-作业题6-(…

嵌入式中的MCU、ARM、DSP、FPGA

目录 “角色扮演” MCU ARM 特点 DSP 特点 FPGA 特点 应用 “角色扮演” MCU&#xff08;Microcontroller Unit&#xff09;、ARM&#xff08;Advanced RISC Machine&#xff09;、DSP&#xff08;Digital Signal Processor&#xff09;和FPGA&#xff08;Field-Progr…

DevOps与CI/CD的最佳实践

在当今的软件开发领域&#xff0c;DevOps&#xff08;开发与运维的结合&#xff09;和CI/CD&#xff08;持续集成/持续交付&#xff09;已经成为了不可或缺的一部分。它们不仅提高了软件开发的效率&#xff0c;还帮助团队更快地交付高质量的软件。本文将深入探讨DevOps文化和CI…