怎样建立链表并同时造成内存泄露
所谓内存泄露(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);
这条语句所申请的内存。