【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,一经查实,立即删除!

相关文章

HTTP 请求的请求体是什么

HTTP 请求的请求体(request body)是指在 HTTP 请求中除了请求行(request line)、请求头(headers)之外的实际数据部分。请求体通常包含用于向服务器发送数据的信息,这些信息可以是表单数据、JSON 对象、XML 文档或其他任何形式的数据。 http请求一般包含的内容 HTTP(超…

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;进行窗…

Git 总结

文章目录 1、基础操作1_提交命令2_创建分支命令3_切换分支4_分支合并5_其他关键命令 2、高级操作1_查看 HEAD 指向2_分离Head3_查看提交记录的hash值4_相对引用5_撤销变更 3、移动提交记录1_整理提交记录2_交互式 rebase 4、经验之谈1_提交技巧12_提交的技巧23_Git Tags4_Git D…

五、事务和并发控制及索引和性能优化

一. 事务和并发控制是数据库管理系统中用于处理多个用户并发访问共享数据的重要机制。 下面是对事务和并发控制的详细讲解和示例说明&#xff1a;事务&#xff1a; 事务是一组数据库操作的逻辑单元&#xff0c;它要么全部执行成功&#xff0c;要么全部回滚。事务通过保证数据操…

Java中的集合(1)——List、Map和Set

Java标准库自带的java.util包提供了集合类&#xff1a;Collection&#xff0c;它是除Map外所有其他集合类的根接口。Java的java.util包中提供了以下三种类型的集合&#xff1a; List&#xff1a;一种有序列表的集合&#xff0c;例如&#xff0c;按索引排列的Student的List&…

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;即可看到下载选项。

jenkins 用ssh 启动nohup java -jar显示执行成功 但是jar包没有被启动起来 (已解决)

问题描述 使用jenkins自动部署jar包. 打包传到服务后, 停止stop.sh脚本执行成功. 并且 xx.jar 也成功停止. 但是使用jenkins执行start.sh脚本执行成功, 但是服务器上xx.jar并没有启动起来. 启动命令是 nohup java -jar **.jar --spring.cloud.bootstrap.namebootstrap-debug&…

demo_GAN

# 导入PyTorch库&#xff0c;这是一个用于深度学习的开源库 import torch # 导入PyTorch的神经网络模块&#xff08;nn&#xff09;&#xff0c;用于定义神经网络结构 import torch.nn as nn # 导入PyTorch的函数式模块&#xff08;functional&#xff09;&#xff0c;提供了一…

把其他.ui文件拿到我的工程中使用

在Qt工程中使用工程外的ui文件的方式&#xff1a;作为一个类直接使用、包含到自己的类中或继承使用 将ui文件添加到工程中&#xff0c;作为一个类以直接使用 注&#xff1a;这里指使用原本不属于该工程的ui文件第一步&#xff1a;在工程文件.pro中添加UI文件 在.proj文件中添加…

每日一题——第一百一十七题

题目&#xff1a;使用二分查找&#xff0c;查找一个数是否存在于一个升序数组中 #include <stdio.h>int binarySearch(int arr[], int length, int elem);int main() {int arr[] { 2, 3, 4, 5, 6, 7, 8, 9, 10 };int key;int length sizeof(arr) / sizeof(arr[0]);pri…

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…

FFMPEG录屏(17)--- 使用 DwmRegisterThumbnail 捕获指定窗口图像数据

使用 DwmRegisterThumbnail 捕获指定窗口图像数据 在 Windows 平台上&#xff0c;捕获指定窗口的图像数据可以通过多种方法实现&#xff0c;其中一种高效的方法是使用 [DwmRegisterThumbnail] 本文将介绍如何使用 [DwmRegisterThumbnail] 捕获窗口图像数据&#xff0c;并提供一…

Mysql中表字段VARCHAR(N)类型及长度的解释

本文将针对MySQL 中 varchar (N)类型字段的存储方式进行解释&#xff0c;主要是对字符和字节的关系的理解。 1. varchar (N) 中的 N varchar (N) 中的 N 表示字符数&#xff0c;而不是字节数。这意味着 N 表示你可以存储多少个字符。 字符数&#xff1a;指的是字符的个数&…

计算机视觉在疲劳检测中的应用

计算机视觉在疲劳检测中的应用 引言 随着科技的飞速发展&#xff0c;计算机视觉技术已经广泛应用于各个领域&#xff0c;其中疲劳检测是近年来备受关注的一个研究方向。疲劳检测旨在通过计算机视觉技术&#xff0c;实时分析个体的面部特征、动作以及生理信号等&#xff0c;判…

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

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