所謂內存泄露(Memery leak),指的是程序向操作系統申請了一塊內存,但並不使用這塊內存或使用完畢之后並不把這塊內存歸還給操作系統,同時放棄了對這塊內存的跟蹤與控制。這樣,這塊內存就成了斷了線的風箏一樣,操作系統認為這塊內存在被使用,所以不可能再把這塊內存拿出來給程序使用,但由於程序已經放棄對它的跟蹤與控制,事實上它已經不可能再被使用了,成了一塊白白耗費系統資源的“內存垃圾”。
內存泄漏的直接后果是減少可用內存數量,從而降低計算機的性能。嚴重時可以導致全部或部分計算機設備停止正常工作,或者應用程序崩潰。
因此,程序員們把內存泄露視為一種很嚴重的BUG,在寫代碼時往往會如履薄冰小心翼翼地避免出現內存泄露。
由於這種BUG很難查找,甚至有很多專門查找內存泄露的工具軟件被發明出來幫助程序員減少這類錯誤。
在這樣的大環境下,現在懂得如何“主動”制造內存泄露這門技術的人是越來越少了。各種計算機技術資料往往只告訴你如何避免內存泄露而根本不告訴你如何制造內存泄露。這就把那些渴望學習內存泄露技術的人們陷入了一種資料缺乏的境地。
有幸的是我最近發現了一本介紹內存泄露技術的教科書,完整地傳授了制造內存泄露的全部過程。鑒於資料稀有,所以益加珍貴。“好東西不敢獨享”,特獻出來與大家分享。
#include <stdio.h>
#include <stdlib.h>
#define LEN sizeof(struct Student)
struct Student
{long num;
float score;
struct Student *next;
};
int n;
struct Student *creat(void)
{struct Student *head;
struct Student *p1,*p2;
n=0;
p1=p2=(struct Student *)malloc(LEN);
scanf("%ld,%f",&p1->num,&p1->score);
head=NULL;
while(p1->num!=0)
{n=n+1;
if(n==1)head=p1;
else p2->next=p1;
p2=p1;
p1=(struct Student*)malloc(LEN);
scanf("%ld,%f",&p1->num,&p1->score);
}
p2->next=NULL;
return(head);
}
int main()
{ struct Student *pt;
pt=creat();
printf("\nnum:%ld\nscore:%5.1f\n",pt->num,pt->score);//輸出第1個結點的成員值
return 0;
}
————譚浩強 ,《C程序設計》(第四版),清華大學出版社,2010年6月,p313~314
首先來測試一下這段代碼。運行程序,然后輸入
0,30
結果一鳴驚人。
程序員通常把自己寫的程序在運行時出現這種東東視為一種奇恥大辱,但樣本代碼的作者大概是想借此顯示一下如何讓程序具有一種纖弱的、弱不禁風的氣質(哦,我見尤憐)。這種氣質與健壯性相反,它力圖表現出一種只要稍有風吹草動立刻就癱倒崩潰的風范。
其實要想達到這種境界並不很難,只要忽視帶代碼的執行條件就可以了(就如同不管三七二十一地把一個數當除數,而不管它是否可能等於0一樣)。比如
printf("\nnum:%ld\nscore:%5.1f\n",pt->num,pt->score);
這句代碼執行的前提條件是pt不為NULL,所以強健的代碼應該這樣寫
if( pt != NULL ) printf("\nnum:%ld\nscore:%5.1f\n",pt->num,pt->score);
你只要把限制前提的“if( pt != NULL )”部分刪除就可以立竿見影地把程序變得具有經不起任何風吹草動的弱不禁風的氣質。
但是不要以為這就是內存泄露技術,真正的內存泄露技術並不體現在這里,而是在creat()這個函數部分。在這個函數中,
p1=p2=(struct Student *)malloc(LEN);
的意思是向操作系統申請內存並用p1,p2這兩個變量記錄其位置。但是緊接着,如果你在執行
scanf("%ld,%f",&p1->num,&p1->score);
時(其實執行這條語句也是有前提條件的,作者在這里為我們再次演示了弱不禁風的代碼氣質)輸入
0,30
就會導致p1->num!=0 這個表達式的值為0,因而while語句直接結束。在while語句之后,程序又毫無意義地執行了
p2->next=NULL;
之后,返回了main()。由於p1,p2是非static局部變量,函數調用結束后這些變量不復存在;又由於p1,p2是唯一記錄原先所申請內存的變量,所以在函數調用結束后,程序成功地將原先申請的那塊內存泄露了,它神奇地“隱身”了、消失了,現在就連神仙都找不到它了。
但如果你以為這種情況僅僅是在一開始就輸入“0,30”這樣顯得有些惡搞的測試時才發生,那你就錯了。這段程序在你不惡搞的情況下同樣可以出色地完成內存泄露的任務。這時它泄露的是
p1=(struct Student*)malloc(LEN);
這條語句所申請的內存。