C++特性——引用与指针详解

news2025/2/26 18:43:38

文章目录

  • 引用
    • 1. 引用的作用
      • 1.1 引用可以做函数参数:
      • 1.2 引用做函数返回值:
    • 2 常引用
    • 3 引用和指针
      • 3.1 引用和指针在语法层面和底层的异同
      • 3.1 引用和指针的不同

引用

简单来说,引用就是给一个变量起一个别名。例如:

int a = 1;
int& b = a;

对于上面的代码,我们就说ba的别名,我们可以看看ba的地址:

在这里插入图片描述

我们可以发现,别名ba共用一块地址,不会开辟新的空间,我们可以将下面三段代码进行比较:

//代码一
int a = 1;
int& b = a;

//代码二
int a = 1;
int b = a;

//代码三
int a = 1;
int* b = &a;
  • 代码一:ba别名,前面已经讲过,这里不再赘述

  • 代码二:创建了一个新的整形变量b,并将a的值赋值给b

  • 代码三:创建了一个指针变量b指向变量a

我们可以画图来更加明确他们的关系:

在这里插入图片描述

1. 引用的作用

1.1 引用可以做函数参数:

既然我们知道引用就表示给一个变量取别名,那么我们就可以用引用做函数实参,来简化用C语言写时较为复杂的代码,例如:

  • 交换两个数的值Swap
/*
	形参a,b是两个别名,和实参共用一个地址。
	因此形参的改变就会影响实参的改变
	故可以不要像C语言一样传实参的地址
*/
void Swap(int& a, int& b)
{
	int temp = a;
	a = b;
	b = temp;
}
  • 无哨兵位单链表的尾插SLPushBack
typedef struct ListNode
{
	struct ListNode* next;
	int val;
}SL;

//如果不用别名,那么形参就应该是二级指针,只有这样才能改变原本为一级指针的实参
void SLPushBack(SL*& phead, int data)
{
	assert(phead);

	SL* newNode = (SL*)malloc(sizeof(SL));
	newNode->val = data;
	newNode->next = NULL;

	if (phead == NULL)
		phead = newNode;
	else
	{
		SL* cur = phead;
		while (cur->next)
			cur = cur->next;

		cur->next = newNode;
	}
}

总结:

引用做函数实参,有以下好处:

  1. 形参的改变可以影响实参,这样可以降低函数的复杂度并提高函数的可读性
  2. 对于大型的实参,如果将它的别名作为形参传入函数,就可以大大提高函数效率

1.2 引用做函数返回值:

先来看一串代码:

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

int main()
{
	int& ret = Add(1, 2);
	cout << ret << endl;

	Add(3, 4);
	cout << ret << endl;

	return 0;
}

大家来想一想,这两次打印的ret的结果分别是什么?

在这里插入图片描述

第一次为3大家可能认为没问题,但为什么第二次明明ret没有接收函数Add(3, 4)的返回值,为什么他的值会变成7呢?

原因是:

  • Add函数的返回值类型是一个引用,即局部变量c的别名,而接收对象ret也是引用,因此ret也是局部变量c的别名
  • 所以尽管第二次ret没有接受返回值,但是c的改变也会影响其别名ret的改变,所以最后ret的值也会变为7。

其实,上面的分析并不完全正确

  • 应该清楚,随着函数栈帧的销毁,其函数内部的局部变量也会被销毁
  • 因此ret打印出的结果应该是不确定的
  • 这里之所以可以打印出看似正确的结果3和7,可能因为博主用的编译器并没有清理函数Add的栈帧

而要使代码正确,我们可以将变量c设置为静态变量,这样就不会随着函数栈帧的销毁而被清除了。

清楚了这一点,再来看下面的两串代码:

代码一:

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

int main()
{
	int& ret = Add(1, 2);
	cout <<"ret = " <<  ret << endl;

	Add(3, 4);

	cout << "ret = " << ret << endl;

	return 0;
}

代码二:

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

int main()
{
	int& ret = Add(1, 2);
	cout <<"ret = " <<  ret << endl;

	Add(3, 4);

	cout << "ret = " << ret << endl;

	return 0;
}

大家认为,这两串代码打印的结果一样吗?

在这里插入图片描述

如果想错了的小伙伴,可能忽略了一点:静态变量只能被初始化一次

因此,对于代码一,定义静态变量c时,将其初始化为a + b = 3,那么之后就不会被初始化,ret的值也就不会变了。

经过上面的一系列分析,可以得出结论:

如果将局部变量的引用作为返回值,那么出了函数作用域,返回对象就被销毁了,不能用引用返回,否则结果就是不正确的。

接下来我们举一个正确使用引用做返回值的例子:

//功能:将顺序表的每个值++

typedef struct SeqList
{
	int data[100];
	int size;
}SL;

int& SLPosModify(SL* sl, int pos)
{
	return (sl->data)[pos];
    
    //返回顺序表每个位置的别名,从而改变返回对象就可以改变顺序表内的内容
}

int main()
{
	SL* sl = (SL*)malloc(sizeof(SL));
	sl->size = 100;
	memset(sl->data, 0, sizeof(int) * sl->size);

	for (int i = 0; i < sl->size; i++)
		SLPosModify(sl, i)++;

	return 0;
}

可以看到,引用做函数返回值有以下好处:

  1. 可以直接修改返回对象
  2. 可以简化代码,提高代码可读性
  3. 可以提高效率

2 常引用

常引用(const reference)是一种引用变量的方式,用于表示引用的目标对象不可被修改

需要注意以下几点:

  1. 取别名时不能将原来的权限放大,只能权限平移或者权限缩小。例如:

    const int a = 1;
    int& b = a;	//权限放大
    

    会有如下报错信息:

    在这里插入图片描述

    而下面的写法就是正确的:

    int a = 1;
    const int& b = a;	//权限缩小
    const int& c = b;	//权限平移
    
  2. 可以给常量取别名。例如:

    const int& d = 10;
    
  3. 当引用涉及到类型转换时,要注意临时变量的常性。例如:

    int a = 1;
    double& b = a;
    

    错误的。而

    int a = 1;
    const double& b = a;
    

    就是正确的

    • 这是由于

      当给整型变量a取一个浮点型的别名b时,就会发生隐式转换,中间就会产生一个临时变量
      而由于临时变量具有常性(const),因此由上面我们所说的取别名不能放大权限,因此正确的写法为const double& b = a;

    • 注:具体的,临时变量到底是怎么产生的以及他有什么特性,感兴趣可以看看👉C/C++陷阱——临时变量的产生和特性

3 引用和指针

在上面的介绍中,我们例举了很多引用替代指针来实现指针功能的例子。那么我们是否可以这样认为:C++中,引用可以完全取代指针?

答案是:不能!

首先我们应该清除引用有两点特性:

  1. 引用在定义时必须被初始化

  2. 引用不能被修改指向,只能修改其值(例如,如果c原来是a的引用,那么c就只能是a的引用)

例如:

在这里插入图片描述

而我们又清楚,指针可以随意改变它的指向,这是引用无法做到的,因此在C++中,引用是无法完全取代只针的,应该说,引用和指针是互补的。

3.1 引用和指针在语法层面和底层的异同

  1. 语法上引用就是给一个变量取别名,不会开辟额外空间,和原来的变量共用一个地址。而指针就会开辟空间用来存储这个指针变量。
  2. 底层上,引用和指针一样,都是用汇编实现的。因此我们可以说,实际上声明和指针都是传地址

那么日常情况下,我们是以语法为主还是底层为主呢?

答案是以语法为主。我们来看一个例子:

char a = 1;
char& b = a;

cout << sizeof(b) << endl;

32位系统,如果输出的是4,那就说明以底层为主,如果输出为1,那就说明以语法为主。

在这里插入图片描述

我们可以看到,输出的结果是1,因此日常情况下,以语法为主

3.1 引用和指针的不同

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址

  2. 引用在定义时必须初始化,指针没有要求

  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体

  4. 没有 NULL 引用,但有 NULL 指针

  5. 在 sizeof 中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)

  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

  7. 有多级指针,但是没有多级引用(一个变量可以有多个引用)

  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理

  9. 引用比指针使用起来相对更安全,但不是绝对安全


本篇完。

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

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

相关文章

【数之道 05】走进神经网络模型、机器学习的世界

神经网络 神经网络&#xff08;ANN&#xff09;神经网络基础激活函数 神经网络如何通过训练提高预测准确度逆向参数调整法 &#xff08;BackPropagation&#xff09;梯度下降法链式法则增加一层 b站视频连接 神经网络&#xff08;ANN&#xff09; 最简单的例子&#xff0c;视…

解锁机器人技术的钥匙—《应用机器人学:运动学、动力学与控制技术》

随着科技的飞速发展&#xff0c;机器人已经广泛应用于各个领域&#xff0c;成为了当今世界的热门话题。作为一个工程师&#xff0c;如果你想在机器人行业大显身手&#xff0c;深入了解机器人运动学、动力学和控制技术是必不可少的。而《应用机器人学&#xff1a;运动学、动力学…

ERROR 1366 (HY000): Incorrect string value,mysql插入数据报错?安排

1.报错 mysql57&#xff0c;windows环境&#xff0c;插入中文数据报错 ERROR 1366 (HY000): Incorrect string value: \xC0\xEE\xCB\xC4 for column nm at row 1 2.测试语句 创建一个数据库&#xff0c;创建表&#xff0c;插入中文数据报错 1366 --创建数据库 CREATE DATABASE…

【每日一题】同积元组

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;哈希表组合 其他语言python3 写在最后 Tag 【哈希表组合】【数组】【2023-10-19】 题目来源 1726. 同积元组 题目解读 在一个由不同正整数组成的数组中找出满足 a * b c * d 的四元组 (a, b, c, d)&#xff0c;返回…

macOS查端口占用进程

java开发人员&#xff0c;端口冲突的问题基本都遇到过吧&#xff01;以下的日志是否熟悉&#xff1a; *************************** APPLICATION FAILED TO START ***************************Description:The Tomcat connector configured to listen on port 8084 failed to …

Spring Security过滤器链分析-初始化流程(8)

过滤器链分析 提起Spring Security的实现原理&#xff0c;很多读者都会想到过滤器链。因为Spring Security中的所有功能都是通过过滤器来实现的&#xff0c;这些过滤器组成一个完整的过滤器链。那么&#xff0c;这些过滤器 链是如何初始化的&#xff1f;我们前面反复提到的Aut…

【YOLO】拾遗(五)

0 YOLO系列笔记 【YOLO】朴实无华的yolov5环境配置&#xff08;一&#xff09; 【YOLO】yolov5训练自己的数据集&#xff08;二&#xff09; 【YOLO】目标识别模型的导出和opencv部署&#xff08;三&#xff09; 【YOLO】语义分割和实例分割&#xff08;四&#xff09; 1 …

Android-NDK-clang 编译 FFmpeg

Android-NDK-clang 编译 FFmpeg Android-NDK-clang 编译 FFmpeg Android-NDK-clang 编译 FFmpeg - 知乎 (zhihu.com) 前期准备 下载 Android-NDK下载 FFmpeg 源码 注意&#xff1a;笔者用的是 NDK-21 和 ffmpeg-4.4 进行编译&#xff0c;如果版本不同可能会有所不同。 测试&a…

【网络爬虫】2 初探网络爬虫

爬虫练手 把豆瓣的书评list页爬取下来&#xff0c;并获取其书名&#xff0c;和detail的连接地址 豆瓣的书评list的url地址&#xff0c; start1,2,3,4…是其地址页 https://book.douban.com/top250?start1 f12 观察其html结构 思路 按照找到的list的页面地址: 1.获取list页…

训练ChatGPT提示词,实现Excel函数操作

Excel常用表格数据处理都会离不开函数的应用。 在数据处理,数据汇总,数据展示的过程中经常需要各类函数的使用如Vlookup,Sumifs,IF 等。 例如有一份数据我们想根据销售经理的名字,查找对应的销售额。 我们先简单描述一下我们的需求: 帮我写个Excel函数,要求查找出任意销…

Electron之集成vue+vite开发桌面程序

在electron中集成vue开发桌面程序 使用我们之前创建的electron项目 创建vue 项目 命令行进入electron根目录 执行下面命令 npm create vitelatest vue -- --template vue这样就创建了一个vue项目&#xff0c;文件名是vue&#xff0c;命令行进入vue下&#xff0c;执行下面命…

react-router-dom v6版本实现Tabs路由缓存切换

目录 文章目录 概要 效果 完整代码 概要 摆了半年摊&#xff0c;好久没写代码了&#xff0c;今天有人问我怎么实现React-Router-dom类似标签页缓存。后面看了一下router的官网。很久以前用的是react-router v5那个比较容易实现。v6变化挺大&#xff0c;但了解react的机制和rea…

工业交换机定制化解决方案:光路科技的工业PoE交换机

PoE交换机能代替普通工业交换机使用吗&#xff1f; Power over Ethernet (PoE)交换机和普通工业交换机在某些情况下可以互相替代&#xff0c;但有些情况下则不太适合。PoE交换机具有额外的供电能力&#xff0c;用于同时为网络设备提供数据和电力。如果您的应用中只需要传输数据…

python web开发(四): Bootstrap

1.初步了解 别人已经写好的CSS样式&#xff0c;我们可以直接引用 下载 Link-BootStrap 解压&#xff0c;并放入到当前项目中 引用 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</tit…

IT售前“楠“知识之这!就是售前-尚文网络xUP楠哥

进Q群11372462领取专属报名福利 &#xff01;&#xff01;&#xff01; # 何为售前工程师 售前工程师在一个IT信息化团队中起到了呈上启下的绝对重要作用&#xff01;站在销售团队的视角&#xff0c;需要售前工程师从技术维度支持销售业务的开展&#xff1b;站在对立面用户的…

一些bug总结

今天被几个小问题和bug折磨了一天&#xff0c;来总结一下… 权限问题 用vscode连接服务器&#xff0c;如果是在root用户连接的情况下新建的文件/文件夹&#xff0c;然后切换到别的用户的时候去写的代码 可能会遇到各种问题 解决方案是更改文件或文件夹的所有权。这可以通过使用…

pymysql连接Mariadb/Mysql出现错误(配置正确情况下)解决办法

场景&#xff1a;在kali中使用python中pymysql对Mariadb进行连接&#xff0c;在整个过程中配置全部正确&#xff0c;但是就是无法进行连接&#xff0c;提示结果如下&#xff1a; Access denied for user rootlocalhost解决办法&#xff1a;进入数据库中&#xff0c;将默认密码…

一步一步分析ChatGPT,1 粘性,2 传染性, 3 双边网络效应

请按照以下三个维度一步一步分析ChatGPT&#xff0c;1 粘性&#xff0c;2 传染性&#xff0c; 3 双边网络效应&#xff0c;比如亚马逊的买家和商家的关系 ChatGPT的分析 1.1. 粘性 (Stickiness) 定义&#xff1a; 粘性是指产品或服务对用户的吸引力&#xff0c;即用户在使用…

番外8.1 配置+管理文件系统

Task01: Linux 文件系统结构&#xff1b; 可以进行Linux操作系统的文件权限管理与方式切换&#xff0c;可以应用磁盘与文件权限管理工具&#xff1b; 01&#xff1a;常见文件系统类型&#xff08;Ext4[rhel6默认文件管理系统], 存储容量1 EB1073741824 GB; XFS[rhel 7/8默认的文…

HugeGraph 部署和Hubble1.0.0的数据导入Bug修复

背景 HugeGraph 安装部署了最新版本1.0.0&#xff0c;发现它的 Web 工具 Hubble 有一个大 Bug。数据导入的时候&#xff0c;配置节点属性映射这个选项时&#xff0c;下拉框只有一个选项&#xff0c;但实际上&#xff0c;元数据配置中的属性有3个&#xff0c;这个 Bug 是怎么产…