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. 如果需要使用调用接口的方式释放指针空间时,需要使用二级指针或者引用传递指针变量的方式,也就是直接传递指针变量的地址。如果直接使用指针传递,能够正常回收空间,但是无法将实参指针置为空值,导致其成为野指针,从而引起内存问题。
相关文章:
C++ 指针变量做参数传递时的情况分析
前言 指针变量作为参数传递时,很容易混淆指针本身和指针指向的内容,实际应用中可能会导致无法预料的问题,所以做一下详细分析。 注意,在测试过程中为了看测试效果,有些指针变量分配了空间,但是未做回收&am…...

Linux环境下Oracle 11g的离线安装与配置历程
在成功体验了 Windows 版本的Oracle 11g 后,这几天心血来潮,决定再挑战一下Linux 环境下的安装,特别是在考虑到部门内部虚拟机无法联网的情况下,我选择了在CentOS 7上进行离线安装。这次安装之旅,主要参考了下面大佬的…...

上位机图像处理和嵌入式模块部署(mcu项目2:串口日志记录器)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 淘宝上面有一个商品蛮好玩的,那就是日志记录器。说是记录器,其实就是一个模块,这个模块的输入是一个ttl串口&am…...

容器是线程不安全的,如果多线程下不加锁直接使用容器会发什么
可能存在的问题 数据竞争 当两个或多个线程同时读写同一个容器且至少有一个线程在写时,会导致数据竞争。这种情况下,容器的内部状态可能会被破坏,从而导致未定义行为。这些未定义的行为包括数据损坏,程序崩溃,以及无…...

配置光源——笔记
一、灯光的类型 (一)Directional Light(定向光) 1、只改变方向变化,不记录位置变化 2、相当于太阳光 3、室外一般使用 (二)Spot 聚光灯:昏暗(凌晨或傍晚),有一个光斑…...

Java---SpringBoot详解一
人性本善亦本恶, 喜怒哀乐显真情。 寒冬暖夏皆有道, 善恶终归一念间。 善念慈悲天下广, 恶行自缚梦难安。 人心如镜自省照, 善恶分明照乾坤。 目录 一,入门程序 ①,创建springboot工程&#…...

MFC扩展库BCGControlBar Pro v35.0 - 可视化管理主题等全新升级
BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中,并为您节省数百个开发和调试时间。 BCGControlBar专业版 v35.0已全新发布了,这个版本改进类Visual Studio 2022的视觉主题、增强对多个…...
Springboot 配置 log4j2 时的注意事项
感谢博主 https://www.cnblogs.com/fishlittle/p/17950944 依赖 SpringBoot 的 spring-boot-starter/ spring-boot-starter-web 自带的是 logback 日志,若要使用 log4j2 日志,需要引入对应依赖。logback 日志和 log4j2 日志都是对 slf4j 门面的实现&am…...

微服务-初级篇
微服务-初级篇 认识微服务1.1 单体架构1.2 分布式架构1.3 微服务 SpringCloud2.1 了解2.2 服务拆分原则2.3 服务拆分效果 Nacos注册中心3.1 认识和安装Nacos3.1.1 Nacos下载3.1.2 Nacos安装 3.2 服务注册到Nacos Feign远程调用4.1 Feign引入4.2 Feign配置 认识微服务 1.1 单体…...

批量制作word表格
问题背景 将excel表中的成绩内容制作为成绩单,每页对应一个学员的成绩,方便打印 代码实现 ## 导入包 import pandas as pd from docx import Document from docx.enum.text import WD_ALIGN_PARAGRAPH,WD_LINE_SPACING# 读取 Excel 内容 df pd.read_e…...
代码随想录算法训练营:27/60
非科班学习算法day27 | LeetCode455:分发饼干 ,Leetcode376:摆动序列 ,Leetcode53:最大子数组和 介绍 包含LC的两道题目,还有相应概念的补充。 相关图解和更多版本: 代码随想录 (programmercarl.com)https://programmercarl.c…...

Redis 中String类型操作命令(命令演示,时间复杂度,返回值,注意事项)
String 类型 文章目录 String 类型set 命令get 命令mset 命令mget 命令get 和 mget 的区别incr 命令incrby 命令decr 命令decrby 命令incrbyfloat 命令append 命令getrange 命令setrange 命令 字符串类型是 Redis 中最基础的数据类型,在讲解命令之前,我们…...

2024亚太杯中文赛B题洪水灾害的数据分析与预测原创论文分享
大家好,从昨天肝到现在,终于完成了2024年第十四届 APMCM 亚太地区大学生数学建模竞赛B题洪水灾害的数据分析与预测的完整论文啦。 实在精力有限,具体的讲解大家可以去讲解视频: 2024亚太杯中文赛B题洪水灾害预测原创论文保姆级教…...

Oracle 19c 统一审计表清理
zabbix 收到SYSAUX表空间告警超过90%告警,最后面给出的清理方法只适合ORACLE 统一审计表的清理,传统审计表的清理SYS.AUD$不适合,请注意。 SQL> Col tablespace_name for a30 Col used_pct for a10 Set line 120 pages 120 select total.…...

PostgreSQL(二十二)缓冲区管理器
目录 一、缓冲区概述 1、缓冲区结构 2、buffer_tag结构 3、Backend进程读取操作 4、写脏块 二、缓冲区管理器结构 1、第一层:Buffer Table layer(缓冲区表层) 2、第二层:Buffer Descriptor Layer(缓冲区描述层…...

流程制造业与离散制造业有何差异?流程行业智能制造关注什么?
在当今快速发展的工业领域,智能制造已经成为推动制造业转型升级的关键力量。随着“工业4.0”概念的提出,智能制造的理念和技术被广泛应用于各个制造行业,包括离散制造业和流程制造业。尽管智能制造的起源和发展在很大程度上受到了离散制造业的…...

【论文速读】《面向深度学习的联合消息传递与自编码器》,无线AI的挑战和解决思路
这篇文章来自华为的渥太华无线先进系统能力中心和无线技术实验室,作者中有大名鼎鼎的童文。 一、自编码架构的全局收发机面临的主要问题 文章对我比较有启发的地方,是提到自编码架构的全局收发机面临的主要问题: 问题一:基于随…...

C++从入门到起飞之——输入输出!
目录 1.命名空间 1.1namespace的价值 1.2namespace的定义 1.3命名空间使⽤ 2.C输⼊&输出 3.完结散花 个人主页:秋风起,再归来~ C从入门到起飞 个人格言:悟已往之不谏,知来者犹可追 克心守己…...

米文AD10配置gmsl摄像头操作
一、进入桌面快捷方式 0、设置摄像头型号 miivii_websettings.desktop 设置摄像头 1、获取camera信息 cat /var/log/gmsl_camera.lognvidiamiivii-tegra:~$ cat /var/log/gmsl_camera.log attestationVerify [13] succeed. [INFO ]: miivii gmsl service start! [INFO ]: V…...

【Selenium配置】WebDriver安装浏览器驱动(ChromeEdge)
【Selenium配置】WebDriver安装浏览器驱动(Chrome&Edge) 文章目录 【Selenium配置】WebDriver安装浏览器驱动(Chrome&Edge)Chrome确认Chrome版本下载对应driver把解压后的chromedriver文件放在chrome安装目录下࿰…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...