【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;//输出3return 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;//第一次输出还是正常值1cout << 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;//intcout << typeid(c).name() << endl;//charcout << typeid(d).name() << endl;//intreturn 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;//intreturn 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.rhkb.cn/news/173099.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

震惊!原来BUG是这么理解的!什么是BUG?软件错误(BUG)的概念

较为官方的概念&#xff1a; 当且仅当规格说明是存在的并且正确&#xff0c;程序与规格说明之间的 不匹配才是错误。 当需求规格说明书没有提到的功能&#xff0c;判断标准以最终用户为准&#xff1a;当程序没有实现其最终用户合理预期的 功能要求时&#xff0c;就是软…

【Python · PyTorch】线性代数 微积分

本文采用Python及PyTorch版本如下&#xff1a; Python&#xff1a;3.9.0 PyTorch&#xff1a;2.0.1cpu 本文为博主自用知识点提纲&#xff0c;无过于具体介绍&#xff0c;详细内容请参考其他文章。 线性代数 & 微积分 1. 线性代数1.1 基础1.1.1 标量1.1.2 向量长度&…

JEnv使用初体验

Java多版本控制器初体验 1、前言 由于公司项目使用jdk8版本&#xff0c;而日常学习会使用其他版本例如jdk17等&#xff0c;往常都是修改环境配置目录实现。 2、下载资料 链接&#xff1a;https://pan.baidu.com/s/1UqzHv8K8WBu-75Ysyc_h3A 提取码&#xff1a;ra6a 3、安装 …

目前和未来的缓存构建

说起来可能有点反直觉&#xff0c;有时候不运行反而可以帮助我们加快速度&#xff0c;这正是网络浏览器运行的指导原则。不必在页面上加载所有内容&#xff0c;缓存的元素已经存在&#xff0c;不需要每次访问网站或网页时都重新加载。页面加载速度越快&#xff0c;浏览器的工作…

汇编学习(1)

汇编、CPU架构、指令集、硬编码之间的关系 ● 汇编语言&#xff1a;这是一种低级语言&#xff0c;用于与硬件直接交互。它是由人类可读的机器码或指令组成的&#xff0c;这些指令告诉CPU如何执行特定的任务。每条汇编指令都有一个对应的机器码指令&#xff0c;CPU可以理解和执…

Ps:对象选择工具

对象选择工具 Object Selection Tool是 Photoshop 2020 版以后新增的选区工具&#xff0c;可用于自动选择图像中的对象或区域&#xff0c;如人物、汽车、宠物、天空、水、建筑物和山脉等。 快捷键&#xff1a;W 让对象选择工具自动检测并选择图像内的对象或区域&#xff0c;或者…

CTF-php特性绕过

注意&#xff1a;null0 正确 nullflase 错误 Extract变量覆盖 <?php$flagxxx; extract($_GET);if(isset($shiyan)){ $contenttrim(file_get_contents($flag));//trim移除引号if($shiyan$content){ echoctf{xxx}; }else{ echoOh.no;} }?> extract() 函数从数组中将…

真实感渲染的非正式调研与近期热门研究分享

真实感渲染的非正式调研与近期热门研究分享 1 期刊1 Top2 Venues 2 Rendering Reserach1 Material2 BRDF3 Appearance Modeling4 Capture5 Light Transport光线传播6 Differetiable Rendring-可微渲染7 Ray Tracing8 Denoising降噪9 NeRF 3 VR/AR4 Non-Photorealistic Renderin…

matlab simulink PMSM永磁电机DTC控制

1、内容简介 略 10-可以交流、咨询、答疑 2、内容说明 PMSM永磁电机DTC控制 PMSM、永磁电机、DTC控制 传 是 &#xff0c;它的工作原理是&#xff1a;首先设定好运行期望值和滞环的容差值&#xff0c;然后把电机 到的差值 号输出&#xff0c;可以 示需要减小 大输出…

10000字!图解机器学习特征工程

文章目录 引言特征工程1.特征类型1.1 结构化 vs 非结构化数据1.2 定量 vs 定性数据 2.数据清洗2.1 数据对齐2.2 缺失值处理 原文链接&#xff1a;https://www.showmeai.tech/article-detail/208 作者&#xff1a;showmeAI 引言 上图为大家熟悉的机器学习建模流程图&#xff0c;…

计算机中了locked勒索病毒怎么办,locked勒索病毒解密,数据恢复

当下网络技术飞速发展&#xff0c;但同样带来的网络安全威胁也不断增加&#xff0c;其中较为明显的威胁就是locked勒索病毒&#xff0c;自从今年以来&#xff0c;很多企业的计算机都遭受到了locked勒索病毒攻击&#xff0c;导致企业的计算机系统瘫痪。通过云天数据恢复工程师对…

【解决方案】ubuntu 解决办法 ImportError: cannot import name ‘_gi‘ from ‘gi‘

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

37回溯算法-理论基础

目录 什么是回溯算法 基本思想 问题场景 回溯算法的理解 回溯算法模板 LeetCode之路——257. 二叉树的所有路径 分析 什么是回溯算法 回溯算法是一种解决组合优化问题、搜索问题以及决策问题的算法。它通常用于尝试在一组可能的解决方案中搜索并找到满足特定条件的解。…

H5游戏源码分享-色块选择游戏

H5游戏源码分享-色块选择游戏 玩到后面色块越来越小&#xff0c;越来越难找出 <!DOCTYPE html><html><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><meta charset"UTF-8"><meta na…

【LeetCode每日一题合集】2023.10.23-2023.10.29(简单的一周)

文章目录 2678. 老人的数目&#xff08;简单遍历模拟&#xff09;1155. 掷骰子等于目标和的方法数&#xff08;动态规划&#xff09;2698. 求一个整数的惩罚数&#xff08;预处理dfs回溯&#xff09;2520. 统计能整除数字的位数&#xff08;简单模拟&#xff09;1465. 切割后面…

C++系列之list的模拟实现

&#x1f497; &#x1f497; 博客:小怡同学 &#x1f497; &#x1f497; 个人简介:编程小萌新 &#x1f497; &#x1f497; 如果博客对大家有用的话&#xff0c;请点赞关注再收藏 &#x1f31e; list的节点类 template struct list_Node { public: list_Node* _prev; list_…

67 内网安全-域横向smbwmi明文或hash传递

#知识点1: windows2012以上版本默认关闭wdigest&#xff0c;攻击者无法从内存中获取明文密码windows2012以下版本如安装KB2871997补丁&#xff0c;同样也会导致无法获取明文密码针对以上情况&#xff0c;我们提供了4种方式解决此类问题 1.利用哈希hash传递(pth&#xff0c;ptk等…

CCF CSP认证历年题目自练 Day39

题目 试题编号&#xff1a; 201312-5 试题名称&#xff1a; I’m stuck! 时间限制&#xff1a; 1.0s 内存限制&#xff1a; 256.0MB 问题描述&#xff1a; 问题描述   给定一个R行C列的地图&#xff0c;地图的每一个方格可能是’#’, ‘’, ‘-’, ‘|’, ‘.’, ‘S’, ‘…

C/C++跨平台构建工具CMake-----灵活添加库并实现开发和生产环境的分离

目录 1.概述2.创建项目3 配置运行项目3.1 编写开平方根示例代码3.2 编写CMake构建脚本 4.使用子模块实现求平方根的功能4.1 在子模块中实现两种求平方根的方法4.2 构建Mathfunctions子模块4.3 在根目录引用子模块的功能4.3.1 编写构建脚本4.3.2 编写C代码使用MathFunctions库中…

qt高精度定时器的使用停止线程应用

##线程停止 //线程停止应用 public: explicit WorkerThread(QObject *parent 0) :QThread(parent), m_bStopped(false){qDebug() << "Worker Thread : " << QThread::currentThreadId();}~WorkerThread(){stop();quit();wait();}void stop() {qDebug()…