【C++】C++当中的复合类型——引用和指针

C++当中的复合类型

最近开始系统地学习 C++ 的语法,参考的主要资料来自于 C++ Primer 第五版,对于学习过程中所遇到的较难理解的点,我会以blog的形式对问题和内容进行记录,并进行进一步地探讨。

这一部分的内容对应于参考资料 C++ Primer 的第二章第三节。

1. 引用

开篇说到,C++11 标准下新增了“右值引用”,这将会在之后进行探讨。目前本章所接触到的引用都是“左值引用”,也就是C++当中大家最常接触到的引用。以我个人的学习经验来说,目前所接触到的引用大多以函数形参出现,它将会作为传入参数的别名进行传址调用,部分代替了 C 语言当中的指针(作为函数形参出现时其代替指针的作用最为明显,因为不需要再对指针进行解除引用了,可以直接使用函数的参数进行传址调用)。

引用是变量的别名

引用(reference) 是为变量起的别名,在对引用类型的变量进行声明的时候,应当在变量名之前加上&。在声明一个引用类型的变量时,必须要对引用变量进行初始化,这一点也非常好理解,因为如果不进行初始化的话,我们声明的引用变量不知道自己是谁的别名,当然会产生错误。

有关引用声明的一个例子如下:

#include <iostream>using namespace std;int main() 
{int a = 23;int &b = a;cout << a << ' ' << b;return 0;
}

输出的结果是:

23 23

如果此时我们再声明一个整型的引用变量c,并且不对它进行初始化,那么我们将得到以下错误信息:

'c' declared as reference but not initialized

需要注意的是,由于引用是变量的别名,它在初始化的时候已经与其它变量绑定,因此如果对引用进行赋值,实际上就是将右侧变量的值赋给了与引用绑定的对象,而不是重新指定了引用所绑定的对象。

引用自身不是一个对象,因此不能定义引用的引用。

总结一下,引用类型在声明的时候必须得到初始化,并且一经初始化,该引用将与变量永久绑定,可以对引用进行赋值,但是由于引用在初始化时与变量永久绑定,为引用的赋值相当于将=的右值对应的值赋给了引用所绑定的对象。引用并非对象,它只是为一个已经存在的对象所起的另外一个名字。定义一个引用之后,对其进行的所有操作都是在与之绑定的对象上进行的,以下是一个例子:

#include <iostream>using namespace std;int main() 
{int a = 23;int &b = a;int c = 32;b += c;cout << a << ' ' << b;return 0;
}

输出的结果当然是:

55 55

引用的定义

与指针相同,可以在一条语句中进行多个引用的定义,但是在每一个引用类型的声明之前都要加上&,比如int &a = b, &c = d;。将&与变量名“紧挨”在一起可以避免歧义。

引用只能与对象进行绑定,因为它是对象的别名,而不能将引用绑定到表达式或是字面值上,因为引用不是变量。

2. 指针

指针(pointer) 是指向(point to)另外一种类型的复合类型。与引用类似,指针也可以完成对变量的间接访问,但是指针是一种更加复杂的复合类型,指针本身指向的是变量的地址,使用指针引用解除符号*可以对指针所指向变量的值进行访问和修改。

指针与引用有着本质上的不同,方才已经提到,指针指向的是变量的地址,而引用只是变量的别名。指针和引用的区别可以总结如下:

  1. 指针本身也是一个对象,允许对指针进行赋值和拷贝,并且在指针的生命周期内可以先后指向多个不同的变量的地址。与之相比,引用只是变量的别名,它必须得到初始化(如果不对指针进行初始化,它将会指向一个随机的地址,这是危险的,可以使用字面值nullptr来使得指针指向空地址,这样的初始化方法是安全的),并且引用类型一经声明无法改变与其绑定的变量,所有对引用的赋值操作都是直接将值赋给了引用绑定的对象。
  2. 无需在指针定义时为指针赋值,指针与其它内置类型相似,在块作用域内定义的指针如果没有得到初始化,也将拥有一个不确定的值(但是这样做是危险的,正如方才所提到的)。

定义一个指针的方法是在变量名之前加上*,比如int *d;定义了一个整型变量的指针d,由于没有进行初始化,它将指向随机值。与引用的声明类似,在声明多个指针类型的变量时,应该为每一个指针变量都加上*,比如int *d, *b;

获取对象的地址

指针类型变量存放的是对象的地址,而想要获取对象的地址,需要使用取地址符&。从外表上看来,变量的取地址符与引用类型的声明是一样的,但是正如这句话所说的,引用是在声明阶段需要使用&来表示它是一个引用类型,而取变量的地址并不是在声明阶段,而可能出现在初始化或是赋值阶段,比如对于整型变量b,它的地址是&b

一个有关指针的例子如下,在这里涉及到变量的取地址符、指针的赋值以及使用指针对变量的值进行修改:

#include <iostream>using namespace std;int main() 
{int a = 23;int *b = &a;	// 使用取地址符为变量赋值cout << *b << ' ' << a + 1 << endl;cout << *b << ' ' << a << endl;*b = *b + 1;	// 解除指针, 对该地址对应的变量(也就是a)进行修改cout << a;		// 得到修改后的结果return 0;
}

实验的结果是:

23 24
23 23
24

需要注意的是,刚才已经反复强调过,引用不是变量而是变量的别名,因此不能定义指向引用的指针。指针所指向的变量必须与指针所声明的类型相同,比如整型的指针必须指向整型变量,如果指向的是double型的变量就会报错。

指针值

指针的值应该属于以下四种状态之一:

  1. 指向一个对象(的地址);
  2. 指向紧邻对象所占空间的下一个位置;
  3. 空指针(nullptr);
  4. 无效指针,即上述三种情况之外的值;

试图拷贝或以其他方式访问无效指针的值都将发生错误,但是编译器不会检查这种错误。

利用指针访问对象

如果指针指向了一个对象,则允许使用解引用符*来访问该对象。对指针解引用会得到所指的对象,因此如果给解引用的结果赋值,实际上也就是给指针所指向的对象赋值。一个实际的例子是调用C++的库函数max_elementmin_element来得到可迭代对象(比如vector)当中的最大值或最小值的地址,使用解引用符可以直接得到对应的最大值/最小值,比如对于某个整型的vectorvint MAX = *max_element(v.begin(), v.end());int MIN = *min_element(v.begin(), v.end());

&*这样的符号既可以作为表达式当中的运算符,又可以作为声明的一部分出现,符号的上下文才能够决定符号的具体意义。例如:

int i = 42;
int &r = i; // 声明一个变量 i 的引用
int *p = nullptr;
p = &i; // 让整型的指针 p 指向变量 i 的地址
*p = i; // 为指针 p 所指向的值重新赋值为变量 i 的值
int &r = *p; // 声明一个整型引用 r, 它是 p 所指向的值的别名

需要注意的是,C++当中可以声明指针的引用,方法是int *&t = p;,即可声明整型指针p的别名t。一个比较难理解的点是区分&**&,经过资料的查阅,较好的解决方案是从右向左对声明语句进行阅读。举个例子,对于int *&t = p;t左侧最先出现的是&,代表t是一个引用,即当前声明的是某种类型的变量的别名,那么类型如何确定呢?继续向右读,发现*,说明这是一个指针类型,再向右读可以确定这个引用是一个整型指针的别名。int *&t = p;声明的是指针类型的引用。

而对于&*,由于引用类型不具有地址,因此无法声明引用类型的指针,int &*t = p;这条语句是不合法的。同样从右往左读,首先读到*代表t是指针类型,而再右侧是int &,即整型的引用,引用没有地址,所以这条语句不合法。

在这里插入图片描述

空指针

空指针不指向任何值,nullptr是C++11新标准引入的新的字面值,代表空指针。在新标准下,应该尽可能地使用nullptr来表示空指针而非使用NULL

赋值和指针

引用和指针都可以对它们所指向的对象进行改变。二者本质的区别是引用本身不是一个对象,不具有地址,它只是它在被初始化时所绑定到其它变量上的别名。而指针是一个复合类型的对象,它具有自己的地址,它的值代表的是它所指向的值的地址,对指针变量使用*可以解除引用,直接对指针所指向的对象进行访问。

指针所指向的对象可以修改,方法是对指针进行赋值,使它的值等于另一个变量的地址或空指针。

其它指针操作

可以使用==!=比较两个合法的指针,返回值是bool类型,代表二者所指向的地址是否相等。

void* 指针

void*是一种特殊的指针类型,可以存放任意对象的地址。与具有类型的指针相比,这种指针与它们最大的不同就是并不了解存放的是何种类型对象的地址。

使用void*指针能做的事情非常有限,可以拿它和其它指针进行比较(地址是否相同)、作为函数的输入输出(传址),或是将它保存的地址赋给另外一个void*类型的指针。

不能直接操作void*指针所指的对象,因为我们不知道它的具体类型。

3. 理解符合类型的声明

变量的定义包括一个基本数据类型(base type)和一组声明符。在同一条声明语句当中,基本类型只能有一个,但是声明符的形式可以非常不同

int i = 1024, *p = &i, &r = i; // 基本类型为 int, 
// 声明了一个 int 型的变量 i, 一个 指向 int 类型变量的指针 p 并将它初始化为 i 的地址
// 以及声明了一个 int 类型的引用 r, 它是 i 的别名.

指向指针的指针

通常来说,声明符当中修饰符的数量不加以限制,因此可以定义“指向指针的指针”,比如:

int ival = 1024;
int *pi = &ival;
int **ppi = &pi;

指向指针的引用

方才已经说过,指针变量是对象,它具有自己的地址,因此它可以有别名,称为“指向指针的引用”:

int i = 2048;
int *p = &i;
int *&r = p;

再次重复一下,想要理解r究竟是什么,最好的做法是从右向左阅读。距离变量名最近的符号对变量的类型有着最直接的影响,因此r首先是一个引用。声明符的其余部分确定了r引用的是何种类型,因此可以确定r是一个指向指针的引用。

不存在指向引用的指针,因为引用不是对象,没有地址。

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

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

相关文章

spring-cloud-alibaba-nacos-config2023.0.1.*启动打印配置文件内容

**背景&#xff1a;**在开发测试过程中如果可以打印出配置文件的内容&#xff0c;方便确认配置是否准确&#xff1b;那么如何才可以打印出来呢&#xff1b; spring-cloud-alibaba-nacos-config 调整日志级别 logging:level:com.alibaba.cloud.nacos.configdata.NacosConfigD…

Linux操作系统与windows无法相互复制问题

请先看完此文在进行操作&#xff01;&#xff01;&#xff01; 对于无法复制我们逐层分析&#xff1a; 1.为什么无法复制是不是少了什么工具(open-vm-tools-destop) 上网查阅可以看到如下 2.在此之前我的虚拟机装完Ubuntu 16.04的linux系统无法进行apt update(参考一下) li…

华三服务器R4900 G5在图形界面使用PMC阵列卡(P460-B4)创建RAID,并安装系统(中文教程)

环境以用户需求安装Centos7.9&#xff0c;服务器使用9块900G硬盘&#xff0c;创建RAID1和RAID6&#xff0c;留一块作为热备盘。 使用笔记本通过HDM管理口&#xff08;&#xff09;登录 使用VGA&#xff08;&#xff09;线连接显示器和使用usb线连接键盘鼠标&#xff0c;进行窗…

excel判断某一列(A列)中的数据是否在另一列(B列)中

如B列如果有7个元素&#xff0c;在A列右边的空白列中&#xff0c;输入如下公式&#xff1a; COUNTIF($B$1:$B$7,A1), 其中&#xff0c;$B$1:$B$7代表A列中的所有数据即绝对范围&#xff0c;A1代表B列中的一个单元格.

Servlet(一)

一.什么是servlet Servlet 是一种实现动态页面的技术。 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一个 web app。 1.回顾 动态页面 vs 静态页面 静态页面也就是内容始终固定的页面。即使 用户不同/时间不同/输入的参数不同 , 页面内容也不会发生变化。(除…

从 Microsoft 官网下载 Windows 10

方法一&#xff1a; 打开 Microsoft 官网&#xff1a; 打开开发人员工具&#xff08;按 F12 或右键点击“检查”&#xff09;。 点击“电脑模拟手机”按钮&#xff0c;即下图&#xff1a; 点击后重新加载此网页&#xff0c;即可看到下载选项。

Palo Alto Networks Expedition 未授权SQL注入漏洞复现(CVE-2024-9465)

0x01 产品介绍&#xff1a; Palo Alto Networks Expedition 是一款强大的工具&#xff0c;帮助用户有效地迁移和优化网络安全策略&#xff0c;提升安全管理的效率和效果。它的自动化功能、策略分析和可视化报告使其在网络安全领域中成为一个重要的解决方案。 0x02 漏洞描述&am…

windows下安装、配置neo4j并服务化启动

第一步&#xff1a;下载Neo4j压缩包 官网下载地址&#xff1a;https://neo4j.com/download-center/ &#xff08;官网下载真的非常慢&#xff0c;而且会自己中断&#xff0c;建议从以下链接下载&#xff09; 百度网盘下载地址&#xff1a;链接&#xff1a;https://pan.baid…

周易解读:八卦02,八卦所代表的基本事物

八 卦02 上一节&#xff0c;我是讲完了八卦的卦象的画法的问题。这一节&#xff0c;我来尝试着去讲解八卦所代表的自然事物。 八卦是谁发明的呢&#xff1f;根据《周易说卦传》的说法&#xff0c;八卦是伏羲发明的。伏羲氏仰观天文&#xff0c;俯察地理&#xff0c;从中提取…

项目模块二:日志宏

一、代码展示 二、补充知识 1、LOG(level, format, ...) format 是用于宏识别格式化&#xff0c;类似于 printf("%s", str); 里面的 "%s" ... 不定参&#xff0c;传入宏的参数除了 level, format, 还有不确定个数的参数。 2、红色 \ 由于宏只能写在一…

链上相遇,节点之间的悸动与牵连

公主请阅 1. 返回倒数第 k 个节点1.1 题目说明1.2 题目分析1.3 解法一代码以及解释1.3 解法二代码以及解释 2.相交链表2.1 题目说明示例 1示例 2示例 3 2.2 题目分析2.3 代码部分2.4 代码分析 1. 返回倒数第 k 个节点 题目传送门 1.1 题目说明 题目名称&#xff1a; 面试题 02…

15分钟学 Go 第 10 天:函数参数和返回值

第10天&#xff1a;函数参数和返回值 目标&#xff1a;理解函数如何传递参数 在Go语言中&#xff0c;函数是程序的基本构建块。了解如何传递参数和返回值是编写高效、可复用代码的重要步骤。本文将详细讲解函数参数的类型、传递方式以及如何处理返回值&#xff0c;辅以代码示…

DP—子数组,子串系列 第一弹 -最大子数组和 -环形子数组的最大和 力扣

你好&#xff0c;欢迎阅读我的文章~ 个人主页&#xff1a;Mike 所属专栏&#xff1a;动态规划 ​ 53. 最大子数组和 最大子数组和 ​ 分析: 使用动态规划解决 状态表示: 1.以某个位置为结尾 2.以某个位置为起点 这里使用以某个位置为结尾&#xff0c;结合题目要求&#…

MySQL8.0主从同步报ERROR 13121错误解决方法

由于平台虚拟机宿主机迁移&#xff0c;导致一套MySQL主从库从节点故障&#xff0c;从节点服务终止&#xff0c;在服务启动后&#xff0c;恢复从节点同步服务&#xff0c;发现了如下报错&#xff1a; mysql> show slave status\G; *************************** 1. row *****…

GDAL+C#实现矢量多边形转栅格

1. 开发环境测试 参考C#配置GDAL环境&#xff0c;确保GDAL能使用&#xff0c;步骤简述如下&#xff1a; 创建.NET Framework 4.7.2的控制台应用 注意&#xff1a; 项目路径中不要有中文&#xff0c;否则可能报错&#xff1a;can not find proj.db 在NuGet中安装GDAL 3.9.1和G…

无人机之自主飞行关键技术篇

无人机自主飞行指的是无人机利用先进的算法和传感器&#xff0c;实现自我导航、路径规划、环境感知和自动避障等能力。这种飞行模式大大提升了无人机的智能化水平和操作的自动化程度。 一、传感器技术 传感器是无人机实现自主飞行和数据采集的关键组件&#xff0c;主要包括&a…

C语言复习第3章 函数

目录 一、函数介绍1.1 函数是什么1.2 C语言中函数的分类1.3 函数原型1.4 高内聚 低耦合1.5 C语言main函数的位置 二、函数的参数2.1 实参和形参2.2 函数的参数(实参)可以是表达式2.3 传值与传址(swap函数)2.4 明确形参是实参的临时拷贝2.5 void(如果不写函数返回值 默认是int)2…

python 爬虫 入门 三、登录以及代理。

目录 一、登录 &#xff08;一&#xff09;、登录4399 1.直接使用Cookie 2.使用账号密码进行登录 可选观看内容&#xff0c;使用python对密码进行加密&#xff08;无结果代码&#xff0c;只有过程分析&#xff09; 二、代理 免费代理 后续&#xff1a;协程&#xff0c;…

企业级调度器 LVS

集群和分布式基础知识 系统性能的扩展方式 当一个系统&#xff0c;或一个服务的请求量达到一定的数量级的时候&#xff0c;运行该服务的服务器的性能和资源上限&#xff0c; 很容易成为其性能瓶颈。除了性能问题之外&#xff0c;如果只部署在单台服务器上&#xff0c;在此服务…

gitee建立/取消关联仓库

目录 一、常用指令总结 二、建立关联具体操作 三、取消关联具体操作 一、常用指令总结 首先要选中要关联的文件&#xff0c;右击&#xff0c;选择Git Bash Here。 git remote -v //查看自己的文件有几个关联的仓库git init //初始化文件夹为git可远程建立链接的文件夹…