在前面的第一次使用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博客