在Qt中使用asan检测内存泄漏(一)

在前面的第一次使用address sanitizer记录_g++ -fsanitize=address-CSDN博客,我介绍了如何编译一个支持asan的C++程序,并利用它检测内存非法访问。

本篇博文继续介绍如何在Qt程序中集成asan,并检测内存泄漏。

目录

[1 写一个pri文件,包含asan支持](#1 写一个pri文件,包含asan支持)

[2 在pro文件里通过include(asan.pri)包含asan支持](#2 在pro文件里通过include(asan.pri)包含asan支持)

[3 故意在main.cpp里留下一个内存泄漏的点](#3 故意在main.cpp里留下一个内存泄漏的点)

[4 编译,运行,观察结果](#4 编译,运行,观察结果)

[5 解释](#5 解释)

[6 补记](#6 补记)


1 写一个pri文件,包含asan支持

当然,你也可以把下面的内容直接写道pro文件里,但是考虑到真实的项目往往包含多个so文件。假如你想保证每个so文件的内存泄漏都被检测,那就要在每个pro文件里都写下面的内容。这就很繁琐,容易出错。不如把asan的配置写在一个pri文件里,哪个pro文件需要,就包含这个pri。

bash 复制代码
CONFIG += sanitizer sanitize_address

QMAKE_CXXFLAGS += -fno-omit-frame-pointer
QMAKE_CFLAGS += -fno-omit-frame-pointer

2 在pro文件里通过include(asan.pri)包含asan支持

bash 复制代码
#-------------------------------------------------
#
# Project created by QtCreator 2026-10-20T21:20:51
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = ASAN
TEMPLATE = app

include(asan.pri)

INCLUDEPATH += /opt/gdal/include
LIBS += -L/opt/gdal/lib64 -lgdal
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0


SOURCES += \
        main.cpp

HEADERS +=

3 故意在main.cpp里留下一个内存泄漏的点

main函数中注释掉delete \[\] pImgData一句。

cpp 复制代码
/*#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}*/

#include <gdal_priv.h>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <ctype.h>

char * my_strlwr(char * str)
{
    char * p = str;
    while(*p)
    {
        unsigned char uc = (unsigned char)*p;
        uc = tolower(uc);
        *p = (char)uc;
        p++;
    }

    return str;
}


bool bReadImageMultiBand(unsigned char * & pImgData, int & iWidth, int & iHeight, int & iChannel, char * strPath);
bool bWriteImageData(char *, unsigned char *, int, int, int, int);
void vFindImageTypeGDAL(char *, char *);

int main(void)
{
    GDALAllRegister();
    int iWidth, iHeight, iChannel;
    unsigned char * pImgData;
    if(bReadImageMultiBand(pImgData, iWidth, iHeight, iChannel, "earthRGBRGB.tif"))
    {
        char arr[64] = {0};
        memcpy(arr, "earth.bmp", strlen("earth.bmp"));
        bWriteImageData(arr, pImgData, iWidth, iHeight, iChannel, iChannel);
        //delete [] pImgData;
    }


    return 0;
}

bool bReadImageMultiBand(unsigned char * & pImgData, int & iWidth, int & iHeight, int & iChannel, char * strPath)
{
    GDALDataset * pDataSet = nullptr;
    pDataSet = (GDALDataset *)GDALOpen(strPath, GA_ReadOnly);
    if(pDataSet == nullptr)
    {
        GDALClose(pDataSet);
        return false;
    }

    GDALDataType dt = pDataSet->GetRasterBand(1)->GetRasterDataType();
    iWidth = pDataSet->GetRasterXSize();
    iHeight = pDataSet->GetRasterYSize();
    iChannel = std::min(pDataSet->GetRasterCount(), 3);
    int arrBandMap[] = {1,2,3};
    pImgData = new unsigned char[iWidth * iHeight * iChannel];
    pDataSet->RasterIO(GF_Read, 0,0, iWidth, iHeight, pImgData, iWidth, iHeight, dt, iChannel, arrBandMap, 0,0,0);
    GDALClose(pDataSet);

    return true;
}

bool bWriteImageData(char * strDestPath, unsigned char * pData, int iWidth, int iHeight, int iSrcChannel, int iDestChannel)
{
    char arrType[1024] = {0};
    vFindImageTypeGDAL(strDestPath, arrType);

    if(arrType)
    {
        std::fstream file;
        file.open(strDestPath, std::ios_base::out);
        file.close();
        GDALDriver * pMemDriver = GetGDALDriverManager()->GetDriverByName("MEM");
        if(pMemDriver == nullptr)
            return false;

        GDALDataset * pMemDataSet = pMemDriver->Create("",iWidth,iHeight,iDestChannel,GDT_Byte,NULL);
        GDALRasterBand *pBand = NULL;
        int arrBandMap[3] = { 1, 2, 3 };
        pMemDataSet->RasterIO(GF_Write,
            0,
            0,
            iWidth,
            iHeight,
            pData,
            iWidth,
            iHeight,
            GDT_Byte,
            iDestChannel,
            arrBandMap,
            0,0,0);

        GDALDriver *pDstDriver = NULL;
        pDstDriver = (GDALDriver *)GDALGetDriverByName(arrType);
        if (pDstDriver == NULL)
        {
            GDALClose(pMemDataSet);

            return false;
        }

        GDALDataset * pDataSet = pDstDriver->CreateCopy(strDestPath,pMemDataSet,FALSE, NULL, NULL, NULL);
        if (pDataSet == NULL)
        {
            GDALClose(pMemDataSet);

            return false;
        }

        GDALClose(pDataSet);
        GDALClose(pMemDataSet);

        return true;
    }
    else
    {
        return false;
    }
}

void vFindImageTypeGDAL(char * pDstImgFileName, char * pDst)
{
    char * dstExtension = my_strlwr(strrchr(pDstImgFileName, '.') + 1);

    if (0 == strcmp(dstExtension, "bmp"))
        sprintf(pDst, "%s", "BMP");
    else if (0 == strcmp(dstExtension, "jpg"))
        sprintf(pDst, "%s", "JPEG");
    else if (0 == strcmp(dstExtension, "png"))
        sprintf(pDst, "%s", "PNG");
    else if (0 == strcmp(dstExtension, "tif"))
        sprintf(pDst, "%s", "GTiff");
    else if (0 == strcmp(dstExtension, "gif"))
        sprintf(pDst, "%s", "GIF");
}

4 编译,运行,观察结果

编译后,输入:

bash 复制代码
ASAN_OPTIONS=detect_leaks=1 ./ASAN

输出:

5 解释

在main函数if分支的最后,注释掉了delete \[\] pImgData一句。这使得原来main.cpp70行的

pImgData = new unsigned chariWidth \* iHeight \* iChannel;

申请的内存不能释放,故给出内存泄漏报告。

6 补记

本项目的源码已经上传至【免费】Qt集成asan的示例资源-CSDN下载

读者可能已经注意到了,这个项目还依赖gdal。gdal在中标麒麟的安装,见我以前的博客CMake编译gdal3.5.3_gdal 3.5.-CSDN博客

相关推荐
StarsGreen1 小时前
libva在intel平台上的前端流程简介
linux
慧都产品智能化2 小时前
Qt 表格升级攻略:复用已有编辑器,减少重复开发
c++·qt·编辑器·qt6.3
ShineWinsu2 小时前
对于Qt:QTableWidget常见属性以及QTableWidgetItem类的解析
开发语言·c++·qt·gui·客户端·表格·qtablewidget
ShineWinsu3 小时前
对于Qt:QListWidget常见属性以及QListWidgetItem类的解析
开发语言·c++·qt·客户端·列表·qlistwidget·qlistwidgetitem
坤坤子吖4 小时前
Linux网络编程:自定义应用层协议与 JSON 序列化
linux·服务器·网络·c++·笔记·json
Felven5 小时前
华为鲲鹏920s芯片serdestool工具使用说明
linux·运维·服务器·华为·鲲鹏920·serdestool
莫晓燚6 小时前
mall 电商项目完整部署---实战
linux·运维·docker·容器
咋的啥名都用不了呢~6 小时前
Linux项目部署5(安装Nginx的插件)
linux
CodeYp20196 小时前
第11讲:应用层协议 HTTP
linux·http
爱和冰阔落6 小时前
【Linux网络】JsonCpp 怎么实现序列化?从 Request/Response 到网络计算器消息设计
linux·运维·网络·php