C++ 指针变量做参数传递时的情况分析

前言

指针变量作为参数传递时,很容易混淆指针本身和指针指向的内容,实际应用中可能会导致无法预料的问题,所以做一下详细分析。
注意,在测试过程中为了看测试效果,有些指针变量分配了空间,但是未做回收,实际应用中要注意,分配空间后在合适的位置释放。

1. 指针变量直接作为参数传递

指针变量pVariant 作为实参传递给函数funcPointer,在funcPointer中由形参pParam接收

1.1 代码

void funcPointer(int *pParam)
{qDebug()<<"pParam  自身的地址:"<<&pParam<<"pParam  指向的地址:"<<pParam<<"pParam  值"<<*pParam;// 修改指针所指向地址中的内容*pParam = 2;qDebug()<<"pParam  自身的地址:"<<&pParam<<"pParam  指向的地址:"<<pParam<<"pParam  值"<<*pParam;// 修改pParam的指向pParam = new int;*pParam = 3;qDebug()<<"pParam  自身的地址:"<<&pParam<<"pParam  指向的地址:"<<pParam<<"pParam  值"<<*pParam;
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);int *pVariant = new int;*pVariant = 1;qDebug()<<"pVariant自身的地址:"<<&pVariant<<"pVariant指向的地址:"<<pVariant<<"pVariant值"<<*pVariant;funcPointer(pVariant);qDebug()<<"pVariant自身的地址:"<<&pVariant<<"pVariant指向的地址:"<<pVariant<<"pVariant值"<<*pVariant;return a.exec();
}

1.2 输出结果:

1. pVariant自身的地址: 0x77fe04 pVariant指向的地址: 0x1986708 pVariant值 1
2. pParam  自身的地址: 0x77fdd0 pParam  指向的地址: 0x1986708 pParam  值 1
3. pParam  自身的地址: 0x77fdd0 pParam  指向的地址: 0x1986708 pParam  值 2
4. pParam  自身的地址: 0x77fdd0 pParam  指向的地址: 0x1986848 pParam  值 3
5. pVariant自身的地址: 0x77fe04 pVariant指向的地址: 0x1986708 pVariant值 2

1.2.1 结果分析:

  • 从1,2可以看出,pVariant和pParam 本身的存储地址不相同,指向的地址和pVariant相同,说明pParam是pVariant的一个拷贝。
  • 从2,3,5可以看出,通过函数funcPointer可以成功修改指针所指向的地址中的内容。
  • 从1,3,4,5可以看出,修改形参pParam指向的地址后,不会影响实参pVariant。
    综上,一级指针变量直接做为参数传递时,函数中会产生一个临时拷贝,通过这个拷贝可以修改原指针变量所指向地址的内容,但是不能改变原指针的指向。

1.3 在函数funcPointer最后增加回收空间操作:

    delete pParam;pParam = nullptr;qDebug()<<"pParam  自身的地址:"<<&pParam<<"pParam  指向的地址:"<<pParam;

1.3.1 输出结果:

1. pVariant自身的地址: 0x77fe04 pVariant指向的地址: 0x31265e8 pVariant值 1
2. pParam  自身的地址: 0x77fdd0 pParam  指向的地址: 0x31265e8 pParam  值 1
3. pParam  自身的地址: 0x77fdd0 pParam  指向的地址: 0x31265e8 pParam  值 2
4. pParam  自身的地址: 0x77fdd0 pParam  指向的地址: 0x3126768 pParam  值 3
5. pParam  自身的地址: 0x77fdd0 pParam  指向的地址: 0x0
6. pVariant自身的地址: 0x77fe04 pVariant指向的地址: 0x31265e8 pVariant值 2

从结果可以看出,如果形参pParam 执行new重新分配了空间,即此时pParam 和pVariant指向的地址不同,那么在函数中回收指针空间时只能操作pParam,不会回收指针变量pVariant的空间,所以此时想回收指针变量pVariant空间的话,需要在外部回收。

1.3.2 funcPointer函数中去掉pParam的new操作,增加delete

void funcPointer(int *pParam)
{qDebug()<<"pParam  自身的地址:"<<&pParam<<"pParam  指向的地址:"<<pParam<<"pParam  值"<<*pParam;// 修改指针所指向地址中的内容*pParam = 2;qDebug()<<"pParam  自身的地址:"<<&pParam<<"pParam  指向的地址:"<<pParam<<"pParam  值"<<*pParam;delete pParam;pParam = nullptr;qDebug()<<"pParam  自身的地址:"<<&pParam<<"pParam  指向的地址:"<<pParam;
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);int *pVariant = new int;*pVariant = 1;qDebug()<<"pVariant自身的地址:"<<&pVariant<<"pVariant指向的地址:"<<pVariant<<"pVariant值"<<*pVariant;funcPointer(pVariant);qDebug()<<"pVariant自身的地址:"<<&pVariant<<"pVariant指向的地址:"<<pVariant<<"pVariant值"<<*pVariant;return a.exec();
}

1.3.3 输出结果

pVariant自身的地址: 0x77fe04 pVariant指向的地址: 0x19267e8 pVariant值 1
pParam  自身的地址: 0x77fdd0 pParam  指向的地址: 0x19267e8 pParam  值 1
pParam  自身的地址: 0x77fdd0 pParam  指向的地址: 0x19267e8 pParam  值 2
pParam  自身的地址: 0x77fdd0 pParam  指向的地址: 0x0
pVariant自身的地址: 0x77fe04 pVariant指向的地址: 0x19267e8 pVariant值 1761789996

从结果可以看出,执行 delete pParam; pParam = nullptr;后,pVariant指向的空间也已经被回收,但是pVariant不为空,依然指向原来的地址,获取到了一个未定义的值,这样可能导致程序出现不可控的问题。

2.指针变量作为引用参数传递

2.1 代码

void funcRefPointer(int *&pParam)
{qDebug()<<"pParam  自身的地址:"<<&pParam<<"pParam  指向的地址:"<<pParam<<"pParam  值"<<*pParam;*pParam = 2;qDebug()<<"pParam  自身的地址:"<<&pParam<<"pParam  指向的地址:"<<pParam<<"pParam  值"<<*pParam;pParam = new int;*pParam = 3;qDebug()<<"pParam  自身的地址:"<<&pParam<<"pParam  指向的地址:"<<pParam<<"pParam  值"<<*pParam;
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);int *pVariant = new int;*pVariant = 1;qDebug()<<"pVariant自身的地址:"<<&pVariant<<"pVariant指向的地址:"<<pVariant<<"pVariant值"<<*pVariant;funcRefPointer(pVariant);qDebug()<<"pVariant自身的地址:"<<&pVariant<<"pVariant指向的地址:"<<pVariant<<"pVariant值"<<*pVariant;return a.exec();
}

2.2 输出结果:

1. pVariant自身的地址: 0x77fe04 pVariant指向的地址: 0x34665f8 pVariant值 1
2. pParam  自身的地址: 0x77fe04 pParam  指向的地址: 0x34665f8 pParam  值 1
3. pParam  自身的地址: 0x77fe04 pParam  指向的地址: 0x34665f8 pParam  值 2
4. pParam  自身的地址: 0x77fe04 pParam  指向的地址: 0x3466738 pParam  值 3
5. pVariant自身的地址: 0x77fe04 pVariant指向的地址: 0x3466738 pVariant值 3

2.2.1结果分析:

pParam 和 pVariant的存储地址、指向地址和内容完全一致。
指针变量作为引用传递时,形参pParam是指针变量pVariant的一个别名,对pParam的操作相当于对pVariant直接进行操作。引用传递时可以修改原指针的指向,可以修改指向地址的内容。

2.3在函数funcRefPointer最后增加回收空间操作:

    delete pParam;pParam = nullptr;qDebug()<<"pParam  自身的地址:"<<&pParam<<"pParam  指向的地址:"<<pParam;

2.3.1输出结果:

pVariant自身的地址: 0x77fe04 pVariant指向的地址: 0x3496638 pVariant值 1
pParam  自身的地址: 0x77fe04 pParam  指向的地址: 0x3496638 pParam  值 1
pParam  自身的地址: 0x77fe04 pParam  指向的地址: 0x3496638 pParam  值 2
pParam  自身的地址: 0x77fe04 pParam  指向的地址: 0x3496878 pParam  值 3
pParam  自身的地址: 0x77fe04 pParam  指向的地址: 0x0
pVariant自身的地址: 0x77fe04 pVariant指向的地址: 0x0
从结果可以看出,引用传递指针变量时,在函数中所有对引用变量的操作,都会影响原来的指针,包括delete操作,所以引用传递时,如果确定此指针之后不再使用,那么可以在函数中释放空间并将指针置空。detele指针并置为nullptr后,无法再获取指针指向的内容。

3.使用二级指针传递

传递指针变量的地址

3.1 代码

void funcPPointer(int **pParam)
{qDebug()<<"pParam  自身的地址:"<<&(*pParam)<<"pParam  指向的地址:"<<*pParam<<"pParam  值"<<**pParam;**pParam = 2;qDebug()<<"pParam  自身的地址:"<<&(*pParam)<<"pParam  指向的地址:"<<*pParam<<"pParam  值"<<**pParam;*pParam = new int;**pParam = 3;qDebug()<<"pParam  自身的地址:"<<&(*pParam)<<"pParam  指向的地址:"<<*pParam<<"pParam  值"<<**pParam;
}int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);int *pVariant = new int;*pVariant = 1;qDebug()<<"pVariant自身的地址:"<<&pVariant<<"pVariant指向的地址:"<<pVariant<<"pVariant值"<<*pVariant;funcPPointer(&pVariant);qDebug()<<"pVariant自身的地址:"<<&pVariant<<"pVariant指向的地址:"<<pVariant<<"pVariant值"<<*pVariant;return a.exec();
}

3.2 输出结果

pVariant自身的地址: 0x77fdec pVariant指向的地址: 0x35166c8 pVariant值 1
pParam  自身的地址: 0x77fdec pParam  指向的地址: 0x35166c8 pParam  值 1
pParam  自身的地址: 0x77fdec pParam  指向的地址: 0x35166c8 pParam  值 2
pParam  自身的地址: 0x77fdec pParam  指向的地址: 0x3516748 pParam  值 3
pVariant自身的地址: 0x77fdec pVariant指向的地址: 0x3516748 pVariant值 3

二级指针传递时,输出结果和引用传递一样,因为都相当于传递了原指针的地址。

3.3 在函数funcPPointer最后增加:

    delete (*pParam);(*pParam) = nullptr;qDebug()<<"pParam  自身的地址:"<<&(*pParam)<<"pParam  指向的地址:"<<*pParam;

3.4 输出结果:

pVariant自身的地址: 0x77fe04 pVariant指向的地址: 0x34967a0 pVariant值 1
pParam  自身的地址: 0x77fe04 pParam  指向的地址: 0x34967a0 pParam  值 1
pParam  自身的地址: 0x77fe04 pParam  指向的地址: 0x34967a0 pParam  值 2
pParam  自身的地址: 0x77fe04 pParam  指向的地址: 0x3496730 pParam  值 3
pParam  自身的地址: 0x77fe04 pParam  指向的地址: 0x0
pVariant自身的地址: 0x77fe04 pVariant指向的地址: 0x0

增加delete后的输出结果也和引用传递时相同。所以,指针变量作为引用和二级指针的方式传递时,效果相同。

注意事项

1. delete指针后,如果不将指针置为nullptr,会产生野指针,指针仍会指向原来的地址,再次使用可能会导致程序崩溃,或者会得到一个不确定的值,导致程序产生不可控的结果。
2. 使用引用传递或者二级指针传递时,在函数中重新分配空间之前,需要将上一次new分配的空间回收,否则会造成内存泄漏。也就是多次new的时候需要对应的delete。
3. 如果需要使用调用接口的方式释放指针空间时,需要使用二级指针或者引用传递指针变量的方式,也就是直接传递指针变量的地址。如果直接使用指针传递,能够正常回收空间,但是无法将实参指针置为空值,导致其成为野指针,从而引起内存问题。

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

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

相关文章

电气常用知识

1、常开、常闭 在不加外力作用下&#xff0c;展示的状态就是常 因此&#xff0c;常开就是在不加外力作用下的 开的状态&#xff0c;也就是断开的状态 常闭就是在不加外力作用下的闭合的状态&#xff0c;也就是闭合的状态 2、单控双控 单控&#xff1a;一个东西只有两种状态…

Ubuntu 修改~/.bashrc终端选择是否使用annconda环境

首先需要明白的是anaconda虽然自带了python&#xff0c;但安装anaconda后并不会覆盖掉你原来的python&#xff08;pip也是一样的&#xff09;&#xff0c;但安装anaconda后它会把自己的bin目录&#xff08;里面有python、pip、conda等命令&#xff09;加到PATH上&#xff0c;而…

Javassist 修改 jar 包里的 class 文件

前言 Javassist 是一个用于处理 Java 字节码的类库&#xff0c;可以用以修改 class 文件或 jar 包里的 class 文件。 简单来说我们用Java编写的代码是放在 java 格式的代码文件里&#xff0c;在编译的时候会编译为 class 格式的字节码文件&#xff0c;然后一般所有 class 文件…

GitHub每日最火火火项目(7.12)

项目名称&#xff1a;public - apis / public - apis 项目介绍&#xff1a;这是一个集体列表&#xff0c;包含了各种免费的 API。该项目可能致力于收集和整理不同领域的免费 API&#xff0c;为开发者提供便利&#xff0c;使其能够更轻松地获取所需的数据和功能。通过使用这些免…

腾讯面试:let、const解决了什么问题?

前言 今天来聊一聊腾讯的一个面试题&#xff0c;let、const解决了什么问题&#xff1f; let、const解决了什么问题&#xff1f; 我们来分析一下这个问题 首先这个问题问我们let、const解决了什么问题&#xff1f; 我们就需要去分别讲解一个let和const是干什么用的 首先le…

雅思词汇及发音积累 2024.7.12

问卷调查 poll /pəʊl/ 民意调查 survey 调查 sample 样本 respondent 调查对象 interviewee 被采访的人 interview 采访,访谈 questionnaire 调查问卷 observation 观察 rank 排序 rate 打分 classification 分类 hypothesis /haɪˈpɒθəsɪs/ 假设 statistics 统计 sta…

宝马退出价格战,19万买不到i3了

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 宝马退出价格战 这一消息&#xff0c;源自知名汽车博主孙少军。 7月11日他发文称&#xff0c;“因价格战导致门店亏损严重&#xff0c;宝马7月将会开始降量保价。” 第二天他又做了补充&#xff0c…

FastAPI 学习之路(四十二)利用Docker部署发布

我们之前的部署都是基于本地的部署&#xff0c;我们这次来看下&#xff0c;如何使用docker部署我们的fastapi项目。 编写Dockerfile ①&#xff1a;首先编写一个docker镜像的制作文件Dockerfile FROM python:3.10RUN pip install fastapi uvicorn aiofiles sqlalchemy pytho…

数据建设实践之大数据平台(七)安装dolphinscheduler

安装DS 上传安装包到/opt/software目录并解压 [bigdata@node101 software]$ tar -zxvf apache-dolphinscheduler-3.1.9-bin.tar.gz 授权 [bigdata@node101 software]$ chown -R bigdata:bigdata /opt/software/apache-dolphinscheduler-3.1.9-bin 配置install_env.…

【LeetCode】633. 平方数之和

1. 题目 2. 分析 典型双指针的题了&#xff0c;不知道为啥LeetCode会把这题放到二分类别下&#xff1f; 需要知道math.ceil()是向上取整&#xff1b; 3. 代码 class Solution:def judgeSquareSum(self, c: int) -> bool:upper math.ceil(sqrt(c))print(upper)left, ri…

Django+vue自动化测试平台(26)-- 接口自动化测试之连接数据库执行sql

概述 做过接口自动化的朋友都知道&#xff0c;接口自动化很重要的一个环节就是&#xff0c;结果对比。 结果对比又分很多种&#xff1a; 响应结果对比入库对比redis对比等… 今天就来讲讲怎么使用python连接数据&#xff0c;并执行sql查询数据&#xff0c;示例&#xff1a;m…

基于蓝牙iBeacon定位技术的商场3D楼层导视软件功能详解与实施效益

在现代商场的繁华与复杂中&#xff0c;寻找目的地往往令人头疼。维小帮3D楼层导视软件以其创新技术&#xff0c;为顾客带来无缝、直观的跨楼层导航体验&#xff0c;让每一次商场消费都成为享受。 商场3D楼层导视软件功能服务 3D多楼层导视地图&#xff0c;商场布局一览无遗 …

Linux进程——进程的概念

文章目录 PCB进程排队进程标识符pid终止进程获取进程id父进程与子进程 我们在上一节内容中有简单谈到进程的感性理解&#xff0c;他在课本上的概念是&#xff0c;程序的一个执行实例或正在执行的程序 但在本质上&#xff0c;他其实就是一个被分配了系统资源&#xff08;CPU&am…

EtherCAT总线耦合器:在欧姆龙SysmacStudio软件里的配置步骤

EtherCAT总线适配器&#xff1a;在欧姆龙SysmacStudio软件里的配置步骤 EtherCAT总线适配器XD7000作为网络接口&#xff0c;连接主控制器&#xff08;如PLC&#xff09;和其他EtherCAT设备&#xff0c;实现实时、高效的数据交换。通过EtherCAT总线耦合器&#xff0c;用户能够将…

推荐系统数据格式COO Matrix

coo_matrix 是一种稀疏矩阵格式&#xff0c;代表坐标形式&#xff08;Coordinate format&#xff09;。在这种格式中&#xff0c;矩阵的非零元素通过行坐标和列坐标存储&#xff0c;因此适用于存储稀疏矩阵&#xff08;即大部分元素为零的矩阵&#xff09;。这种格式对于构建稀…

类和对象(初)

目录 一、面向过程和面向对象初步认识 二、类的引入 命名规则 三、类的定义 四、访问限定符 五、类的作用域 六、类的实例化 七.类对象模型 一、面向过程和面向对象初步认识 1.C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;…

Java:更适合小白的Java语言开发规范(根据阿里巴巴Java开发手册总结整理)

一 代码原则 通过所有测试&#xff08;Passes its tests&#xff09;&#xff1a;强调的是外部需求&#xff0c;这是代码实现最重要的​尽可能消除重复 (Minimizes duplication)&#xff1a;代码的模块架构设计&#xff0c;保证代码的正交性&#xff0c;保证代码更容易修改尽可…

面试题目分享

学习目标&#xff1a; 从面试了解自己的不足。 学习内容&#xff1a; 1.你会什么语言&#xff1f; 我该如何回答&#xff0c;我会java&#xff0c;c&#xff0c;c等&#xff0c;在工作中我会用到合适的语言。 牛逼吹的大话 尊敬的面试官&#xff0c;我精通Java和Python&…

鸿蒙语言基础类库:【@ohos.data.storage (轻量级存储)】

轻量级存储 轻量级存储为应用提供key-value键值型的文件数据处理能力&#xff0c;支持应用对数据进行轻量级存储及查询。数据存储形式为键值对&#xff0c;键的类型为字符串型&#xff0c;值的存储数据类型包括数字型、字符型、布尔型。 说明&#xff1a; 开发前请熟悉鸿蒙开发…

Android表格布局的概念与属性

表格布局的概念与属性 表格布局&#xff08;TableLayout&#xff09;是以行、列的形式来管理控件的&#xff0c;类似与表格。如图所示&#xff0c;是一个表格布局。TableLayout继承自LinearLayout&#xff0c;支持LinearLayout所支持的全部属性&#xff0c;默认为垂直方向的Li…