1. 程式人生 > >C語言記憶體洩漏檢測方法

C語言記憶體洩漏檢測方法

記憶體洩漏是C語言程式設計中一個很常見的問題,而且由於記憶體洩漏所導致的問題出現較緩慢,所以不容易覺察,所以寫一個簡單的程式來檢測記憶體洩漏很有必要。

     記憶體洩漏通常是指堆記憶體的洩漏,也就是通過malloc、calloc函式申請的記憶體,因此記憶體洩漏的檢測方法核心思想就是通過巨集定義自定義記憶體分配及釋放函式來替換使用者的malloc、calloc、free等函式。設計資料結構記錄記憶體申請資訊,申請記憶體時,記錄並插入到全域性的連結串列中;釋放記憶體時從全域性連結串列中查詢對應的記錄,並刪除。程式結束時,將連結串列中資訊寫入檔案,並清除連結串列。

如下所示,通過巨集定義替換malloc、calloc、free等函式,並插入__FILE__、__LINE__定位語句位置。

#define malloc(size) malloc_detector(size,__FILE__,__LINE__)
#define calloc(element_num,element_size) calloc_detector (element_num,element_size,__FILE__,__LINE__)
#define free(addr) free_detector(addr)

設計記憶體申請資訊如下所示:

typedef struct __info{
    void* addr;                       //the memory address 
    unsigned char file[128];          //the file of the memory alloc statement 
    unsigned int line;                //the line of the memory alloc statment 
    unsigned int size;                //the size of the memory alloced
    }Alloc_Info;

typedef struct __node{
    Alloc_Info info;
    Alloc_Info*next;
    }Alloc_Info_Node,*Alloc_Info_List;


最終資訊被寫入到檔案output_file中,以“w+”模式開啟檔案

#define output_file "Leak_Detector_Report.txt"


在定義被替換的malloc等函式之前,必須通過undef語句將malloc等的巨集定義取消,以防止出現死迴圈的問題,然後才可以定義替換函式

#undef malloc
#undef calloc
#undef free

malloc_detector、free_detector定義如以下程式碼所示。

void *malloc_detector(size_t size,unsigned char *file,unsigned int line)
{
    void *ptr=malloc(size);
    Alloc_Info info;
    info.addr=ptr;
    info.line=line;
    info.size=size;
    strncpy(info.file,file,127);
    add_info(info);                        //Add info to the global Alloc_Info_List head
    return ptr;
}

void free_detector(void *addr)
{ 
    delete_info(addr);                  //Find and Delete the info which has the address==addr from Alloc_Info_List head
    free(addr);
    /*we only detecte the memory-leak, not the wrong free-addr which not in the Alloc_Info_LIst*/
}


程式結束時刻呼叫report_info函式儲存檢測資訊。

void report_info()
{
    FILE *fp_write=fopen(output_file,"w+");
    if(!fp_write){printf("can't open file\n");exit(1);}
    char info[sizeof(Alloc_Info)+128];
    Alloc_Info_List i=head,pre;
    if(i==NULL){
        sprintf(info,"%s","no memory leak");
        fwrite(info,strlen(info)+1,1,fp_write);
    }
    for(i=head;i!=NULL;)
    {
        sprintf(info,"memory leak:file %s line %d,addr %x,size %d\n",i->info.file,i->info.line,i->info.addr,i->info.size);
        fwrite(info,strlen(info)+1,1,fp_write);
        pre=i;
        i=i->next;
        free(pre);
    }
    flcose(fp_write);
}


以下兩個函式為連結串列操作

void add_info(Alloc_Info info); //add memory alloc info to Alloc_Info_List
void delete_info(void *addr);   //find and delete the alloc info     


測試程式如下:

#define DEBUG
#include<stdio.h>
#include<malloc.h>
#include"leak_detector_c.h"
extern Alloc_Info_List head;
int main()
{
    char *ptr1=malloc(100);
    char *ptr2=malloc(101);
    char *ptr3=malloc(102);

    char *ptr4=calloc(103,1);
    char *ptr5=calloc(104,1);
    char *ptr6=calloc(105,1);
    free(ptr1);
    free(ptr6);
    report_info();
}


測試結果如下:

memory leak:file F:\個人原始碼庫\記憶體洩漏處理方法\leak_detctor_c_v2\text.c line 9,addr 32620,size 101
memory leak:file F:\個人原始碼庫\記憶體洩漏處理方法\leak_detctor_c_v2\text.c line 10,addr 32728,size 102
memory leak:file F:\個人原始碼庫\記憶體洩漏處理方法\leak_detctor_c_v2\text.c line 12,addr 32830,size 103
memory leak:file F:\個人原始碼庫\記憶體洩漏處理方法\leak_detctor_c_v2\text.c line 13,addr 33ad0,size 104


    檢測記憶體洩漏時,預設每一個free所對應的地址已經被申請,即已經儲存在全域性列表中,可以對刪除函式進行擴充,如果搜尋完整個列表都沒有發現對應的alloc_info則可以判定為free出錯,對於定位free error有幫助。