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安装目录下࿰…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
MySQL 主从同步异常处理
阅读原文:https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主,遇到的这个错误: Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一,通常表示ÿ…...
用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章
用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章 摘要: 操作系统内核的安全性、稳定性至关重要。传统 Linux 内核模块开发长期依赖于 C 语言,受限于 C 语言本身的内存安全和并发安全问题,开发复杂模块极易引入难以…...
