
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node *next;
}NODE,*PNODE;
PNODE Create2()
{
int i,n;
PNODE pHead,pTail,pNew;
pHead=(PNODE)malloc(sizeof(NODE));
if(NULL==pHead)
exit(-1);
pHead->next=NULL; //生成头结点
pTail=pHead;
printf("请输入数据元素的个数:");
scanf("%d",&n);
for(i=0;i<n;i++)
{
pNew=(PNODE)malloc(sizeof(NODE));
if(NULL==pNew)
exit(-1);
printf("请输入第%d个元素的值:",i+1);
scanf("%d",&pNew->data);
pTail->next=pNew;
pTail=pTail->next;
pNew->next=NULL;
}
return pHead;
}
void Destroy(PNODE pHead)
{ //销毁pHead指向的单链表:头结点也不存在只剩一个头指针,且头指针为NULL
PNODE p;
while(pHead!=NULL)
{
p=pHead->next;
free(pHead);
pHead=p;
}
}
void Traverse(PNODE pHead)
{
PNODE p=pHead->next;
while(p!=NULL)
{
printf("%d ",p->data);
p=p->next;
}
printf("\n");
}
int main()
{
PNODE pHead=Create2();
printf("初始化后的元素:");
Traverse(pHead);
Destroy(pHead);
printf("销毁单链表后:");
Traverse(pHead);
return 0;
}
代码运行后:

通过观察和比较,表面问题出现在导线()的函数中,
两个导线的区别是: 首先在引导节点的单链列表中执行
p=pHead->next;

然后判断p是否为空并输出p->数据;
然后在没有头节点的单链接列表中首先执行它
p=pHead;
然后判断p是否为空并输出p->数据;
两者之间的区别在于头节点的输出是pHead-> data;头节点的输出是pHead-> next-> data;
让我们分析此问题的原因:


注意: 上图显示了在没有头节点的情况下执行清除功能或销毁功能的情况. 具有头节点的Destroy函数的执行图只是在第一个节点之前添加头节点,但是引起的问题是相同的.
首先,在Clear函数中,传递的实际参数为pHead,形式参数也为pHead,这等效于值传递,因此实际参数和形式参数指向相同的元素,但是当链表被销毁,形式参数是pHead正在执行. 当释放第一个节点时,形式参数pHead指向下一个元素,但是实际参数指向第一个节点并已被释放,因此此时实际参数pHead的内容是乱码,不再是0x01,而是遍历时,没有头的节点将首先确定pHead是否为空,因为它是乱码,所以不为空,因此它将输出pHead->数据为乱码,pHead指向其下一个节点,检测到下一个节点是空,并跳出while循环,因此执行结果将看到乱码,并且pHead指向前导节点的循环之前的下一个节点,也就是说,其下一个节点为空,因此它是空的将不会执行while循环并直接结束,并且您不会看到乱码.
如何解决此类问题?
以下提供了两种解决方案(以不带头节点的单链表为例)
第一个: 使用C语言方法

具体代码如下:
void Clear(PNODE *pHead)
{
PNODE p=*pHead;
while(p!=NULL)
{
p=(*pHead)->next;
free(*pHead);
*pHead=p;
}
}
int main()
{
PNODE pHead=Create2();
printf("初始化后元素:");
Traverse(pHead);
printf("销毁链表后:\n");
Clear(&pHead);
Traverse(pHead);
return 0;
}
只需修改以上两个功能
修改后的函数执行图如下:

第一个节点释放时


注意: 形式参数pHead存储实际参数pHead变量的地址,通过在函数中使用* pHead,表示形式参数pHead的内容,即实际参数pHead,可以在前面看到与修改之后的不同之处在于,修改之前,功能的破坏是通过形式参数进行的不带头结点的单链表,而实际参数不会移动. 修改后,实际参数由形式参数控制,实际参数破坏了功能. 这显示了两者之间的本质区别
第二: C ++引用实现
void Clear(PNODE &pHead)
{
PNODE p=pHead;
while(p!=NULL)
{
p=pHead->next;
free(pHead);
pHead=p;
}
}
int main()
{
PNODE pHead=Create2();
printf("初始化后元素:");
Traverse(pHead);
printf("销毁链表后:\n");
Clear(pHead);
Traverse(pHead);
return 0;
}
根据原始错误不带头结点的单链表,您只需要将Clear函数的形式参数更改为(PNODE&pHead).
另一个值是,在没有前导节点的单链接列表上进行操作时,必须以上述两种方式传递参数,这与插入和删除操作中的前导节点单链接列表有重要区别. <
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-188091-1.html