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安装目录下࿰…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
