关于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等问题, ...
随机推荐
- 2023 ICPC 杭州题解
游记 gym F. Top Cluster std 二分答案.需要判断点权 \(\le mid\) 的点到询问点的最大距离.直径. K. Card Game 设 \(f[l,r]\) 为 \([l,r ...
- 如何将Linux的NIC 名称更改为 eth0 而不是 enps33 或 enp0s25,只要几秒钟
概述 我们使用Linux系统,网卡名称通常都是eth0,但是有一些新的linux发行版,网卡名字 enps33 或 enp0s25. peng@ubuntu:~$ ifconfig ens33 Lin ...
- .NET 6 使用Nlog 记录日志到本地并写入SQLserver数据库
1. 安装Nlog 对应Nuget包版本 NLog:5.0.4 NLog.Database:5.0.4 NLog.Web.AspNetCore:5.1.4 Microsoft.Data.SqlClie ...
- 在 Mac 上使用 X11
有时我们需要在服务器上运行一个 GUI 程序,然而我们是通过 SSH 连接到服务器的,看不到图形界面,怎么办呢?我们可以通过 X11 将 GUI 程序的界面转发到本地. 在 Mac 上使用 X11 需 ...
- 【Appium】之自动化定位总结
一.同级定位时,先定位上级 我想定位[必填]框,我先定位[姓名]的同一个上级 self.driver.find_element(MobileBy.XPATH,"//*[contains(@t ...
- Mysql table 调整table的字符集和校对规则
ALTER TABLE `xxxx`.`xxx` CHARACTER SET = utf8mb4 , COLLATE = utf8mb4_0900_ai_ci ;
- Go实现常用的排序算法
一.插入排序 1.从第一个元素开始,该元素可以认为已经被排序 2.取出下一个元素,在已经排序的元素序列中从后向前扫描 3.如果该元素(已排序)大于新元素,将该元素移到下一位置 4.重复步骤3,直到找到 ...
- 如何使用 Redis 实现后台房间的数据管理?
摘要:利用 Redis 实现房间业务管理的实践与思考. 文|即构业务后台开发团队 在一些互动场景中,比如语音聊天室.电商直播等,成员控制.连麦.献花.发弹幕等互动功能,通常要求后台服务器能够储 ...
- OData – API Versioning
前言 先看这 3 篇 ASP.NET Core – Web API Versioning ASP.NET Core – Swagger OpenAPI (Swashbuckle) ASP.NET Co ...
- ASP.NET Core – Web API Versioning
前言 项目持续维护, API 就需要版本控制. ASP.NET Core 有官方的插件专门处理 API 版本控制. 主要参考 Your Guide to REST API Versioning in ...