专业的定制型网站建设电商网站订烟平台
自信、冷静、专注。—— TM 熊的自我勉励
1 背景
在 C/C++软件开发中,不可避免的会遇到内存操作,如果操作不当,很容易造成内存泄漏,对于简单的代码工程,我们可以去检查代码中 new 和 delete 的匹配对数就基本能定位到问题,但是这样的方法对于代码量以万为单位的系统级工程会是致命的,这时我们就需要用到一些内存检测工具,今天我们推荐 Valgrind,一款非常好用的开源内存管理框架,通过实例看看 Valgrind 在内存检测上的简便性和可靠性.
2 常见的内存异常操作
- 申请的内存未释放
- 使用未初始化的内存(使用野指针)
- 对释放后的内存读/写(使用野指针)
- 动态内存越界
- 不匹配地使用 malloc/new/new[] 和 free/delete/delete[]
- 重复释放内存
3 Valgrind 简介
Valgrind 是运行在 Linux 上一套基于仿真技术的程序调试和分析工具。它包含一个内核——一个软件合成的 CPU,和一系列的小工具,每个工具都可以完成一项任务——调试,分析,或测试等。Valgrind 可以检测内存泄漏和内存越界,还可以分析 cache 的使用等,灵活轻巧而又强大。Valgrind 包含以下工具
Memcheck
:检查程序中的内存问题,如泄漏、越界、非法指针等。Callgrind
:检测程序代码覆盖,以及分析程序性能。Cachegrind
:分析 CPU 的 cache 命中率、丢失率,用于进行代码优化。Helgrind
:用于检查多线程程序的竞态条件。DRD
: 也是线程错误检测器。它与 Helgrind 类似,但使用不同的分析技术,因此可能会发现不同的问题Massif
:堆栈分析器,指示程序中使用了多少堆内存等信息。DHAT
: 是一种不同类型的堆分析器。它可以帮助您了解块寿命,块利用率和布局效率低下的问题SGcheck
: 一种实验工具,可以检测堆栈和全局数组的溢出。它的功能与 Memcheck 的功能互补:SGcheck 发现 Memcheck 无法解决的问题,反之亦然BBV
: 一个实验性的 SimPoint 基本块矢量生成器。它对进行计算机体系结构研究和开发的人很有用Lackey
: 一个示例工具,用于说明一些仪器基础知识Nulgrind
: 最小的 Valgrind 工具,不进行分析或检测,仅用于测试目的
即 Valgrind 其实是一个工具集,内存错误检测只是它众多功能的一个,但我们用得最多的功能正是它——memcheck。而学会用 Valgrind 的 memcheck 也正是我们今天的主题。
4 Valgrind 的下载与安装
最新版本链接http://valgrind.org/downloads/valgrind-3.16.0.tar.bz2
Linux 下 Valgrind 的安装
1. tar -jxvf valgrind-3.16.0.tar.bz2
2. cd valgrind-3.16.0
3. ./configure
4. make
5. sudo make install
5 Valgrind 检查程序中的内存问题
- 运行环境:centOS6.9
- 以下代码在编译时加上-g 选项
5.1 申请的内存未释放
#include
#include
int *func(){
return (int *)malloc(sizeof(int));
}
int main(){
int *p = func();
*p = 100;
return 0;
}
可以看到 valgrind 工具的提示:分配了1次四字节内存,释放0次,并标定了申请内存的代码行号。
5.2 使用未初始化的内存(使用野指针)
#include
int main(){
int *p;
int a = *p; //第7行
return 0;
}
可以看到 valgrind 工具的提示:在代码的第7行使用了未初始化的指针,且程序中未出现内存泄漏。
5.3 对释放后的内存读/写(使用野指针)
#include
#include
int *func(){
return (int *)malloc(sizeof(int));
}
int main(){
int a = 0;
int *p = func();
*p = 100;
free(p); //第15行
a = *p; //第17行
return 0;
}

可以看到 valgrind 工具的提示:第17行错误读取*p的值,此时可用空间是0字节,因为指针p已经在第15行被释放了,同时提示该程序是没有内存泄漏的。
5.4 动态内存越界
#include
#include
int main(){
int *str1 = (int *)malloc(sizeof(int)*10);
str1[10] = 100; //第8行
free(str1);
return 0;
}
可以看到 valgrind 工具的提示:在第8行有越界访问。注意:valgrind并不会对在栈上分配的数组越界行为做检查,如下面的例子。
#include
#include
int main(){
int str2[10] = {0};
str2[10] = 200;
return 0;
}

5.5 不匹配地使用 malloc/new/new[] 和 free/delete/delete[]
#include
#include
int *func(){
return (int *)malloc(sizeof(int)); //第6行
}
int main(){
int *p = func(); //第11行
*p = 100;
delete p; //第14行
return 0;
}
可以看到 valgrind 工具的提示:第14行不匹配Mismatched,用malloc分配内存,却用delete释放内存,应该用free。
5.6 重复释放内存
#include
#include
int *func(){
return (int *)malloc(sizeof(int));
}
int main(){
int *p = func();
*p = 100;
free(p); //第14行
free(p); //第15行
return 0;
}
可以看到 valgrind 工具的提示:第15行有一个错误释放,因为第14行时就已经释放掉p指针了,同时提示程序1次malloc,2次free
絮叨
南极熊club专注于嵌入式与物联网领域。由于刚刚创立,一定会存在很多不足的地方,无论是写作风格还是内容上,欢迎大家提出批评与建议。学习不是为了变得全知全能,而是为了不再害怕未知,我是熊二,我们下期见。