关于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” 被赋值的是未知的,导致了 指针悬空 的问题。

posted @ 2024-09-03 13:18  笑眯眯办大事  阅读(23)  评论(0编辑  收藏  举报