背景
最近学习了一下动态链表,第一次编的时候感觉不太对劲,输出结果后光标一直闪着卡在那里,没有显示那个press any button to continue.于是我仔细跟书本核对了一下,果然发现了错误。
具体分析
先看看我第一次写的代码。(输入多个学号与姓名,组成链表,学号为0表示结束,然后全部输出)
#include<stdio.h>
#include<stdlib.h>
struct Student
{ int num;
char name[10];
struct Student* ptr;
} ;
int main()
{
//head 头指针 p1 p2用于链表建立 p用于输出
struct Student *head=0,*p1,*p2,*p;
//n来计算节点数量
int n=0;
//先申请第一个节点 返回给p1 p2
p1=p2=(struct Student*)malloc(sizeof(struct Student));
while(1)
{
scanf("%d",&(p1->num));//输入学号
if(p1->num==0) //输入0 就结束
{
p1->ptr=NULL;//最后一个为空指针
break;
}
scanf("%s",p1->name);//输入姓名
n++;
if(n==1) head=p1;
else p2->ptr=p1;
p2=p1; //p2指向p1
p1=(struct Student*)malloc(sizeof(struct Student));//继续申请
}
p=head;
do //输出链表
{
printf("%d %s\n",p->num,p->name);
p=p->ptr;
}while(p->ptr!=NULL);
return 0;
}
接着我们运行程序,输入 12 wang 45 li ,结果如左图:
过了一会儿 ,才终于显示 press any button to continue ,如右图。
虽然程序能正常显示,但显然是出现了一定的问题,于是我们来对代码进行检查。 不难发现其中一个错误在倒数第二行的while条件,即
while(p->ptr!=NULL)
这里应该改为 while(p!=NULL),原因应该很简单,而错误造成的后果等一下会提到。
此时我们再运行一下程序,然而并没有任何改善,并且我们产生了一个更大的疑问,没有修改之前我们应该只能输出第一行数据,可为什么两行都输出了呢?
带着疑问,我进行了单步调试。
调试
调试过程中,12 wang申请的地址为0x007f1780,45 li 申请的地址为0x007f1740 (申请的地址是由大到小的)。
也就是理想状态下,链表应该是这样的:
num | name | ptr | |
head | - | - | 0x007f1780 |
12 | wang | 0x007f1740 | |
45 | li | NULL |
而实际上,调试出来的结果是这样的:
num | name | ptr | |
head | - | - | 0x007f1780 |
12 | wang | 0x007f1740 | |
45 | li | 0xcdcdcdcd | |
0 | - | NULL |
造成第一次时两行都输出的原因无疑是 0xcdcdcdcd的出现,毕竟他也不是空指针,那么它到底是什么呢?
0xcdcdcdcd
查阅资料,我发现0xcdcdcdcd是表示未被初始化的地址,为一个非法地址。所以调试过程中弹出了这个窗口:
既然他是未初始化的(它理应是NULL,但NULL在下一个指针),说明我们程序运行时将NULL的赋值下移了一位。具体地说,就是这一段的p1->ptr=NULL出现了问题:
if(p1->num==0) //输入为0时
{
p1->ptr=NULL;//最后一个为空指针
break;
}
应该改为 p2->ptr=NULL;修改后结果输出就一切正常了。
释放链表
很多人强调,使用了malloc函数一定要记得free,但是谭浩强的c教材并没有示范如何释放链表,其实释放链表的函数也挺好写的。那就直接上代码吧。
void freeList(struct Student *head)//释放链表
{ struct Student *p1,*p2=head;
while(p1!=NULL)
{
p1=p2->ptr;
free(p2);
p2=p1;
}
}
感觉核心思想跟链表的建立差不多。把freeList()函数放在printf函数之前,看看它是否真正释放掉,如图:
乱码了,说明链表已经被释放了!