关于C++当中的指针悬空问题
一、哪里遇到了这个问题
在进行MNN机器学习框架的MFC应用开发的时候遇到了这个问题,在窗口控件代码段 “MNN_Inference_BarCode_MFCDlg.cpp” 当中需要进行输入图片的读取。通过opnecv2库创建cv:Mat对象,具体代码如下,是一个按钮的控件代码。重点关注其中指针操作的内容
//按钮1,用于选择图片文件
void CMNNInferenceBarCodeMFCDlg::OnBnClickedButton1()
{
CFileDialog fileDlg(TRUE, _T("*.txt"), NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T(" Png Files (*.png)|*.png| Jpg Files (*.jpg)|*.jpg| bmp Files (*.bmp)|*.bmp| BMP Files (*.BMP)|*.BMP| All Files (*.*)|*.*||"));
if (fileDlg.DoModal() == IDOK)
{
NIU::m_configureinfo_Dlg.picture_filepath = fileDlg.GetPathName(); // 保存图片文件路径
SetDlgItemText(IDC_EDIT1, NIU::m_configureinfo_Dlg.picture_filepath);
LoadImagePaths(); // 加载图片路径
// 将CString转换为std::string,用于后续加载图像
std::string imagePathStr(CT2A(NIU::m_configureinfo_Dlg.picture_filepath.GetString()));
// 使用OpenCV加载图像并获取尺寸
cv::Mat image = cv::imread(imagePathStr);
if (!image.empty())
{
// 保存图像的宽度和高度到类的成员变量
NIU::m_imageinfo_Dlg.img_width = image.cols; // 图像的宽度
NIU::m_imageinfo_Dlg.img_height = image.rows; // 图像的高度
// 指向图像数据,这里使用成员变量而不是局部变量是为了保证变量的生命周期,避免当OnBnClickedButton1()函数结束之后,变量被销毁导致指针成为悬空指针
// 将加载的图像存储到成员变量 m_imageMat 中
m_imageMat = image; //请仔细看这里!!!!!!!!!!!!!!
// 指向图像数据
NIU::m_imageinfo_Dlg.img_data_pt = m_imageMat.data;
// 显示图像在图片控件上
DisplayImage(m_imageMat);
}
else
{
AfxMessageBox(_T("无法加载图像文件,请检查文件路径。"));
CButton* check1 = (CButton*)GetDlgItem(IDC_CHECK1);
CButton* check2 = (CButton*)GetDlgItem(IDC_CHECK2);
check1->SetCheck(BST_UNCHECKED); // 取消选中 Check1
check2->SetCheck(BST_UNCHECKED); // 取消选中 Check2
}
}
}
代码当中的img_data_pt为另外一份代码 “AIEngineCommon.cpp” 当中类的一个成员,具体如下:
class NIU
{
public:
// 用于保存输入图像信息的结构体
typedef struct ImageInfo
{
Coordinate_VOC roi_coord; // [输入]ROI坐标
void* img_data_pt; // [输入]图片存储地址
int img_height; // [输入]图像高度
int img_width; // [输入]图像宽度
prep_type_t prep_type; // [输入]前处理类型
};
//声明静态变量
static ImageInfo m_imageinfo_Dlg;
}
可以看到在opencv读取完图像之后,通过 “m_imageMat.data” 这个图像指针指向图像像素数据的首地址,然后将这个指针赋值给NIU类当中的 “img_data_pt” 指针。这样方便图像存储地址可以跨文件调用。
这里需要注意的是这个指向图像像素数据的首地址指针,要么是属于全局变量,要么是数据类当中的成员变量,千万不能是这个函数当中的局部变量。这里设计到两个概念,分别是:
指针有效性
(1)指针有效性是非常重要的概念,涉及到指针是否指向了合法的内存地址。
(2)初始化:指针在使用前应该被初始化。未初始化的指针可能指向任意内存区域,这会导致未定义行为。
(3)分配内存:在使用指针之前,通常需要为其分配内存。例如,在C++中,可以使用 new 关键字为指针分配内存。
(4)释放内存:当指针不再需要时,应该释放它所指向的内存。在C++中,这通常是通过 delete 关键字完成的。如果忘记释放内存,可能会导致内存泄漏。
(5)生命周期:指针的有效性与它所指向的数据的生命周期有关。如果数据被销毁(例如,一个局部变量离开了它的作用域),那么指向它的指针就变得无效。
(6)野指针:如果指针被释放了内存,但没有将其设置为 nullptr,那么它就变成了野指针。野指针指向的是一个不再有效的内存地址,试图访问它可能会导致程序崩溃。
(7)悬挂指针:当指针指向的内存被释放后,如果再次被分配给另一个对象,那么原来的指针就变成了悬挂指针。如果通过悬挂指针访问数据,可能会访问到错误的数据。
(8)指针与对象的关系:指针的有效性也与它所指向的对象的状态有关。如果对象被修改,那么指针可能需要更新以反映这种变化。
(9)多线程环境:在多线程环境中,指针的有效性更加复杂,因为多个线程可能同时访问和修改指针和它所指向的数据。
(10)智能指针:为了避免手动管理内存,可以使用智能指针(如C++中的 std::unique_ptr、std::shared_ptr),它们可以自动管理内存的分配和释放。变量生命周期
(1)局部变量:
定义在函数或代码块内部的变量称为局部变量。
它们的生命周期从定义时开始,到函数或代码块执行结束时结束。
局部变量在函数调用结束后会被销毁,它们通常存储在栈(stack)上。(2)全局变量:
全局变量是在函数外部定义的变量,它们在整个程序的执行期间都是可见的。
它们的生命周期从程序开始执行时开始,到程序结束时结束。
全局变量通常存储在数据段(data segment)或BSS段(如果未初始化)。(3)静态变量:
静态变量是使用 static 关键字声明的变量,它们的生命周期贯穿整个程序的执行期间。
静态局部变量只在定义它们的函数或代码块中可见,每次函数调用时它们都会保留上一次的值。
静态全局变量则在整个程序中可见,但它们的作用域可能被限制在定义它们的文件内。(4)动态分配的变量:
使用动态内存分配(如C++中的 new 或C中的 malloc)创建的变量,它们的生命周期由程序员控制。
必须使用相应的释放函数(如 delete 或 free)来手动管理这些变量的生命周期,否则可能会导致内存泄漏。(5)线程局部变量:
在多线程环境中,线程局部变量是每个线程独有的,它们在线程的生命周期内有效。
线程结束时,线程局部变量会被销毁。(6)对象的成员变量:
对象的成员变量(也称为属性或字段)的生命周期与对象本身相同。
当对象被创建时,成员变量被初始化;当对象被销毁时,成员变量也会随之销毁。(7)自动变量:
在某些编程语言中,如C和C++,自动变量是局部变量的一种,它们在进入作用域时自动创建,在离开作用域时自动销毁。(8)寄存器变量:
寄存器变量是存储在CPU寄存器中的变量,它们通常用于优化性能,因为访问寄存器比访问内存更快。
寄存器变量的生命周期通常与它们所在的作用域相同。(9)常量:
常量是一旦初始化后其值就不能被改变的变量。
它们的生命周期可以是局部的、全局的、静态的等,这取决于它们是如何声明的。
因此,如果上述 “img_data_pt” 指针被赋值的对象是一个局部变量,比如:
// 使用OpenCV加载图像并获取尺寸
cv::Mat image = cv::imread(imagePathStr);
if (!image.empty())
{
NIU::m_imageinfo_Dlg.img_width = image.cols; // 图像的宽度
NIU::m_imageinfo_Dlg.img_height = image.rows; // 图像的高度
m_imageMat = image;
NIU::m_imageinfo_Dlg.img_data_pt = image.data;
// 显示图像在图片控件上
DisplayImage(m_imageMat);
}
那么当该函数结束的时候,CV::Mat image实例就会被销毁,导致 “img_data_pt” 被赋值的是未知的,导致了 指针悬空 的问题。
关于C++当中的指针悬空问题的更多相关文章
- C++中关于指针初始化和使用NULL的理解
1.严禁使用未被初始化的指针:C++创建指针的时候,只分配存储地址的内存,并不会分配存储数据的内存,所以指针可能指向任何位置. (1)使用解除运算符(*)之前,一定要对指针初始化,否则若声明的指针刚好 ...
- 从汇编看c++多重继承中this指针的变化
先来看一下下面的c++源码: #include <iostream> using namespace std; class X { public: virtual void print1( ...
- C编程的指针涛 ---第九笔记
//这里说的是一个指针,指向算法的应用 //直接排序 //每个排序算法是指针指向的每个元件的特性的方便的交流 //这里的基本思想是,处理的记录的排序n - 1第二选择. //第i次操作选择i大(小)的 ...
- C++类指针类型的成员变量的浅复制与深复制
本篇文章旨在阐述C++类的构造,拷贝构造,析构机制,以及指针成员变量指针悬空问题的解决.需要读者有较好的C++基础,熟悉引用,const的相关知识. 引言: 类作为C++语言的一种数据类型,是对C语言 ...
- C++ 智能指针Auto_PTR 分析
C++的动态内存的分配与释放是个挺折磨人的事情,尤其异常分支复杂时(比如一堆try catch中,各catch里需要做delete 掉相关的堆上分配的内存),极有可能产生内存泄露的情况.C++中提供了 ...
- 2-Linux C语言指针与内存-学习笔记
Linux C语言指针与内存 前面我们对于: c语言的基本用法 makeFile文件的使用 main函数的详解 标准输入输出流以及错误流管道 工具与原理 指针与内存都是c语言中的要点与难点 指针 数组 ...
- C++ 指针形参和指针引用形参的原理分析
C++ 函数的参数传递可以分为:值传递和引用传递. 两者的最大区别也很简单,如果该函数的参数只是读的话,值传递就可以满足.如果该函数的参数需要进行修改并返回的时候,就应该进行引用传递. C++指针作为 ...
- C++管理指针成员
1.C++中一般採用以下三种方法之中的一个管理指针成员: (1)指针成员採取常规行为. 这种类具有指针的全部缺陷:具有指针成员且使用默认复制构造函数和赋值操作符,无法避免悬垂指针(两个对象的指针成员指 ...
- C++指针和结构体基础知识
学习C++首先要回忆起C语言当中的指针和结构体知识,本文作者将通过一段代码来总结指针和结构体基础知识:指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址.就像其他变量或常量一样,您必须在使 ...
- Rust所有权及引用
Rust 所有权和借用 Rust之所以可以成为万众瞩目的语言, 就是因为其内存安全性. 在以往内存安全几乎全都是通过GC的方式实现, 但是GC会引来性能.CPU以及Stop The World等问题, ...
随机推荐
- 禅道项目管理系统权限绕过漏洞(QVD-2024-15263)
本文所涉及的任何技术.信息或工具,仅供学习和参考之用,请勿将文章内的相关技术用于非法目的,如有相关非法行为与文章作者无关.请遵守<中华人民共和国网络安全法>. 1. 概述 1.1 基本信息 ...
- .netcore生命周期、消息管道
.NET Core 的初始化过程涉及多个步骤,这些步骤从应用程序的启动开始,一直到应用程序准备好处理请求.下面是一个简化的概述,描述了 .NET Core 应用程序(特别是 ASP.NET Core ...
- Figma 替代品 Penpot 安装和使用教程
在设计领域,Figma 无疑是一个巨人.它彻底改变了设计流程,将协作带到了一个全新的高度.然而,随着 Adobe 收购 Figma 的消息传出,许多设计师和开发者开始担心:Figma 未来会如何演变? ...
- Element ui 动态自定义表格单元格样式
最终实现效果 在Element UI 的文档中提到了用cell-style 方法来自定义单元格样式: 具体使用方法: 1. 在el-table 标签中添加 cell-style 绑定的自定义方法 2. ...
- Vue 子组件修改父组件传递过来的值
实现效果:通过点击选中的按钮控制左边的树是否进行展示 子组件篇: <el-button v-if="isShowTree&hasTree" type="te ...
- 计算机Power电源状态
在计算机电源管理中,S1, S2, S3, S4 代表不同的电源状态或睡眠状态. 了解这些状态,对计算机设备理解功耗及工作状态有很大帮助.最近公司开会,系统同事有讲S3状态功耗很低,我猜和电脑的睡眠. ...
- 淘宝订单信息获取接口API,淘宝打单发货接口
从事电商软件开发的小伙伴,在日常开发任务中,经常会遇到一个需求,就是将淘宝店铺的订单,同步到自己的内部订单管理系统OMS中,进行淘宝打单发货操作.我介绍下如何将订单同步下来,供各位参考.(注意:所有电 ...
- kubernetes删除ns异常状态为:Terminating
在部署kuboard控制平台的时候,不规范删除,导致ns状态为Terminating [root@master01 ~]# kubectl delete namespace kuboard ^C ro ...
- KernelWarehouse:英特尔开源轻量级涨点神器,动态卷积核突破100+ | ICML 2024
动态卷积学习n个静态卷积核的线性混合,加权使用它们输入相关的注意力,表现出比普通卷积更优越的性能.然而,它将卷积参数的数量增加了n倍,因此并不是参数高效的.这导致不能探索n>100的设置(比典型 ...
- Spring —— IoC入门案例
IoC入门案例 思路分析: 1.管理什么?(Service与Dao) 2.如何将被管理的对象告知IoC容器?(配置) 3.被管理的对象交给IoC容器,如何获取到IoC容器? ...