【C语言进阶深度学习记录】三十四 C语言实现内存泄漏检测模块

上一篇文章学习了malloc系列的三个函数的使用。众所周知malloc的使用很容易导致内存泄漏。本文的目的就是使用C语言来实现内存泄漏检测模块,来帮忙自动检测我们写的程序中是否出现内存泄露。

文章目录

    • 1 内存泄露检测模块的实现原理
      • 1.1 各个函数模块的设计
      • 1.2 模块整合
    • 2 总结

1 内存泄露检测模块的实现原理

想要实现内存泄露检测模块,必须在内存申请的时候将申请的内存位置,在代码的哪个位置写代码申请的,以及在哪个源文件中申请的这些信息都记录下来,然后如果申请的内存没有被释放,那就最终将这些没有释放的内存的信息打印出来,我们才能够定位到内存泄露的地方。

可以采用数组记录申请的内存的信息,数组的每个元素存的是申请过的内存的信息。因为信息比较多,所以数组的元素采用结构体的形式。

采用数组的话,申请内存时遍历数组的元素,将申请的内存信息记录到数组中,释放内存的时候遍历数组查找要释放的内存信息对应哪个元素。最后还需要遍历数组查看是否数组中还残留有没有释放的内存由的话,就将泄漏的内存信息打印出来,方便查找。

1.1 各个函数模块的设计

先给出各个小部分函数的设计思想。最后给出整个代码的整合,会让你豁然开朗。可以结合后面的整个代码来看。

  1. 首先定义内存信息的结构体,这个结构体信息可以让我们知道申请的内存的地址,申请的代码在哪个源文件以及哪一行,以及申请的内存的字节大小。如下:
typedef struct{void* pointer; //指针int size;      /大小const char* file;   //文件名int line;     //行号
}MemoryInfo;
  1. 然后定义存放申请到的内存信息的结构体数组。该结构体时用于我们存放申请到的内存信息,以及在最后查找是否有内存没有释放用的:
#define SIZE 256static MemoryInfo M_array[SIZE];
  1. 申请内存的函数设计:标准库函数malloc无法检测内存泄漏,所以我们对malloc进行封装。在申请内存的时候,将申请到的内存信息记录到M_array数组中。参数n是代表申请多少字节内存。file代表当前在哪个源文件中使用了内存申请的函数。参数line是代表在该源文件的哪一行进行了内存申请的动作。
void* mallocEX(size_t n, const char* file, int line){void* ret = malloc(n);if(ret!=NULL){int i = 0;for(i=0; i<SIZE; i++){if(M_array[i].pointer==NULL){M_array[i].pointer = ret;M_array[i].size = n;M_array[i].file = file;M_array[i].line = line;break;}}}return ret;
}

可以看到该函数中将申请到的内存信息都记录到了M_array数组中。方便后续的查询信息。

  1. 释放内存函数设计:释放内存的函数中,必须先遍历数组M_array ,查找要释放的内存在数组中的记录索引,在释放内存的时候,同时将数组中存放该内存的信息置空,代表该内存已经被释放:
void freeEX(void* p){if(p!=NULL){int i = 0;for(i=0; i<SIZE; i++){if(M_array[i].pointer == p){M_array[i].pointer = NULL;M_array[i].size = 0;M_array[i].file = NULL;M_array[i].line = 0;free(p);break;}            }}
}
  1. 查询未释放的内存信息:在程序的结束,即将退出之前,需要查询当前的程序中是否有未释放的内存,如果有将该信息打印出来方便程序员定位修改。直接遍历数组M_array,只要有元素非空,说明就有内存没有被释放:
void PRINT_LEAK_INFO(){int i = 0;printf("Potential Memory Leak Info: \n");for(i=0; i<SIZE; i++){if(M_array[i].pointer != NULL){printf("Address: %p, size:%d, Location: %s:%d\n", M_array[i].pointer, M_array[i].size, M_array[i].file, M_array[i].line);}}
}

1.2 模块整合

上面的几个函数的设计,可以实现内存泄漏检测。将整个程序整合为下面的三个代码:

  • 38-1-lyy.c
#include <stdio.h>
#include "lyy_mleak.h"void f(){MALLOC(100); //申请内存,没有释放 
}int main(){int* p = (int*)MALLOC(3*sizeof(int));f();     //这里发生内存泄漏p[0] = 0;p[1] = 1;p[2] = 2;FREE(p);PRINT_LEAK_INFO();return 0;
}
  • lyy_mleak.h

#ifndef _LYY_MLEAK_H
#define _LYY_MLEAK_H#include <malloc.h>#define MALLOC(n) mallocEX(n, __FILE__, __LINE__)
#define FREE(p) freeEX(p)void* mallocEX(size_t n, const char* file, const int line);
void freeEX(void* p);
void PRINT_LEAK_INFO();#endif
  • lyy_mleak.c
#include "lyy_mleak.h"#define SIZE 256typedef struct{void* pointer;int size;const char* file;int line;
}MemoryInfo;static MemoryInfo M_array[SIZE];void* mallocEX(size_t n, const char* file, int line){void* ret = malloc(n);if(ret!=NULL){int i = 0;for(i=0; i<SIZE; i++){if(M_array[i].pointer==NULL){M_array[i].pointer = ret;M_array[i].size = n;M_array[i].file = file;M_array[i].line = line;break;}}}return ret;
}void freeEX(void* p){if(p!=NULL){int i = 0;for(i=0; i<SIZE; i++){if(M_array[i].pointer == p){M_array[i].pointer = NULL;M_array[i].size = 0;M_array[i].file = NULL;M_array[i].line = 0;free(p);break;}            }}
}void PRINT_LEAK_INFO(){int i = 0;printf("Potential Memory Leak Info: \n");for(i=0; i<SIZE; i++){if(M_array[i].pointer != NULL){printf("Address: %p, size:%d, Location: %s:%d\n", M_array[i].pointer, M_array[i].size, M_array[i].file, M_array[i].line);}}
}
  • 编译运行上述代码:gcc 38-1-lyy.c lyy_mleak.c -o test.out
  • ./test.out

在这里插入图片描述

可以看到,运行程序后,自动的为我们检测是否有未释放的内存,并且将未释放的内存信息打印出来供我们定位修改。只需要将函数f() 修改为以下即可:

void f(){int* p = MALLOC(100); //申请内存,没有释放 ......FREE(p);
}

这样修改过后,就不会出现内存泄漏了。

2 总结

  • malloc和free一定要成对出现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/422778.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用cookie实现叶卡的记忆功能

之前在写叶卡切换的时候总需要把js代码写到html标签里面。 后来接触了闭包后知到一点点怎么通过闭包实现该功能。 之后又想通过cookie来记忆叶卡的当前位置。 代码如下&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http:/…

重学前端----前端知识系统学习推荐专栏

推荐一个前端知识学习专栏。此专栏为极客时间收费专栏。 学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 文章目录1 概述2 专栏内容2.1 模块一&#xff0c;JavaScript2.2 模块二&#xff0c;HTML 和 …

【C语言进阶深度学习记录】三十五 程序中的堆、栈以及静态存储区(数据区)

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 在我之前学习底层的知识的时候&#xff0c;也写过相关的内容。可以对比的学习&#xff1a;【软件开发底层知识修炼】二十 深入理解可执行程序的结构&a…

Silverlight WCF RIA服务(十三)数据 3

如何验证数据 我们对实体和成员属性添加验证属性来实施验证规则。WCF RIA Service提供了几个验证属性来执行常用的验证检测&#xff0c;还提供了CustomValidationAttribute属性来执行自定义的验证检测。如何验证数据我们对实体和成员属性添加验证属性来实施验证规则。WCF RIA S…

【C语言进阶深度学习记录】三十六 程序与进程的区别(程序的内存布局)

上一篇文章学了堆&#xff0c;栈以及静态存储区。它们实际上都是针对进程来说的。那么程序与进程有什么区别呢&#xff1f; 本文不细讲程序与进程。 1 程序与进程 1.1 什么是程序 写完的.c文件是源文件。也叫源代码。 将源代码编译后&#xff0c;会生成可执行文件程序&#…

使用Cucumber+Rspec玩转BDD(2)——邮件激活

使用CucumberRspec玩转BDD(2)——邮件激活 2009年3月2日 星期一 ### 温故知新 ###前面我们已经完成了新用户注册功能的开发&#xff0c;为了方便我们后面的开发工作且不扰乱之前的工作成果&#xff0c;我们先将这份源代码归档并做个标记。为了获得更好的阅读体验&#xff0c;读…

【C语言进阶深度学习记录】三十七 C/C++中造成程序内存错误的原因(野指针)

什么是野指针&#xff1f; 指针变量存的地址是一块非法内存地址。进而形成野指针。但是需要注意一点&#xff0c;野指针不是NULL指针。 文章目录1 野指针的概念1.1 野指针代码案例初探2 如何避免野指针2.1 野指针代码案例分析进阶3 总结1 野指针的概念 野指针变量中的值是非法…

算法补充 2011-9-12

设计一个算法将顺序表L中所有小于0的整数放前半部分&#xff0c;大于等于0的整数放在后半部分二叉树的删除设计一个算法将顺序表L中所有小于0的整数放前半部分&#xff0c;大于等于0的整数放在后半部分 思路:从左侧找出>0的元素&#xff0c;从右侧找出<0的元素,然后进行交…

【C语言进阶深度学习记录】三十八 C/C++语言中的函数声明与函数定义

文章目录1 函数的声明和定义1.1 代码分析2 总结1 函数的声明和定义 声明的意义在于告诉编译器程序单元的存在。只是告诉编译器它存在但是不在声明这里定义&#xff0c;有可能在当前文件中的其他地方或者其他文件中定义。如果在它还没有被定义之前就使用它&#xff0c;会导致编…

ASP.NET MVC3 系列教程 - 部署你的WEB应用到IIS 6.0

I:ASP.NET MVC3 部署的前期工作 1.确认部署的服务器操作系统环境 首先我们确认服务器的操作系统版本可以从系统命令行工具里输入: systeminfo 获取相关操作系统信息例如然后再确认IIS版本信息 -> 打开IIS管理工具即可接着确认.NET Framework的版本可以在系统命令行工具执行:…

【C语言进阶深度学习记录】三十九 C语言中的可变参数(参数可变的函数)

用过printf()函数的热都知道&#xff0c;printf的参数可以有多个&#xff0c;它是可变的&#xff0c;根据我们输出参数的类型以及个数的不同来确定参数。今天来学习C语言中参数可变的函数是如何实现的。 文章目录1 可变参数2 总结1 可变参数 首先我们要明白一点&#xff0c;在…

Linux 安装 OpenOffice

继续弃W从L的奋斗&#xff01;哈哈 在RedHat 6 上安装 OpenOffice。 首先在官网上下载OpenOffice的软件包 100多M。软件包名为&#xff1a;OOo_3.3.0_Linux_x86_install-rpm_en-US.tar.gz 下好后开始安装软件&#xff1a;1 解压 tar xjvf OOo_3.3.0_Linux_x86_install…

【离散数学中的数据结构与算法】一 最大公约数与最小公倍数之间的关系

文章目录1 算数基本定理2 最大公约数3 最小公倍数4 性质5 推论1 算数基本定理 设正整数 n>1&#xff0c; 则 n 可唯一地表示为&#xff1a; 其中 p1<p2<,…, <ps 是 s 个相异的素数&#xff0c; 指数ki都是正整数。 此定理又称作唯一析因定理&#xff08;unique f…

Item 添加事件 list grally等

mainListView.setOnItemClickListener (new OnItemClickListener(){ public void onItemClick(AdapterView<?> parent,View v,int position,long id){ } });转载于:https://www.cnblogs.com/sode/archive/2011/09/25/2189845.html

【离散数学中的数据结构与算法】二 欧几里得算法与裴蜀等式

欧几里得算法是计算两个数最大公因子算法。又称辗转相除法。本文将学习为什么辗转相除法可以求得两个数的最大公因子。同时也可以根据最大公因子计算两个数的最小公倍数。 文章目录1 欧几里得算法的理论基础1.1 欧几里得算法&#xff08;辗转相除法&#xff09;2 裴蜀等式&…

数据库-存储过程-游标-函数

一、存储过程 SQL99标准提出的SQL-invoked-rountines的概念&#xff0c;它开分为存储过程与函数&#xff0c;这里首先介绍存储过程 存储过程分为三类&#xff1a;系统存储过程(如&#xff1a;sp_help)、自定义存储过程、扩展存储过程 存储过程可以理解为一个SQL语句块&am…

【离散数学中的数据结构与算法】四 加法法则与乘法法则

文章目录1 加法法则2 乘法法则3 例子3.1 例一3.2 例二3.3 例三4 总结1 加法法则 加法法则&#xff1a; 设事件 A 有 m 种产生方式&#xff0c; 事件 B 有n 种产生方式&#xff0c;则当 A 与 B 产生的方式不重叠时&#xff0c;“事件 A 或 B 之一” 有 mn 种产生方式。 加法法…

实现上移的存储过程

--上移存储过程 create proc sp_sort id int as declare SortID int --排序位置 declare TempSortID int --临时排序位置 declare TempID int --临时编号 begin transaction select SortIDSortID from [User] where [ID]ID --找出想修改顺序的用户的当前当前排序 select Tem…