继续理解二重指针,删除链表头结点(C语言)

链表复习 2

继续理解二重指针

  • 本以为上一篇写完就理解了二重指针,但是发现在写到删除链表头结点的时候还是不太懂,所以用这篇文章来继续分析一下。
  • 本文的一些假设:
      1. 将单链表分为有头结点和无头结点的情况
      2. 有头结点的情况下不能删除头节点,无头结点可以删除头结点(这个区别是写这篇文章的主要目的)

有头结点的情况

  • 有头结点的情况我们一般都很熟悉,第一个节点通常不存放数据(或者存放一些统计数据),它的指针域存放第一个有效节点的地址。代码如下:

结构体

```
typedef struct Node{
int data;
struct Node *next;
}Node;
```

创建链表头结点

int main()
{
   Node *list=NULL;
   list = CreatNode1();
}

Node * CreatNode1()
{
   Node * head=(Node *)malloc(sizeof(Node));
   head->data=0;
   head->next=NULL;
   return head; 
}

上一篇说过,创建头结点可以用两种方式,另一种方式如下:

int main()
{
   Node *list=NULL;
   CreatNode2(&list);
}

   Node * CreatNode2(Node **head)
{
   Node * pNew=(Node *)malloc(sizeof(Node));
   pNew->data=0;
   pNew->next=NULL;
   *head=pNew;
}

这种创建方式可以通过画图来理解一下:
下图是上一篇提到的错误的创建方法,我们的目的是要修改list的值,但是传参的时候只传了list,这就相当于传值,会有一个形参副本(list’)。在CreatNode函数里,pNew malloc了一个新的空间(假设地址是1000)并把地址给了list’,list’的值由NULL变为1000,但是list的值并没有改变,因为函数执行结束后会把list’释放掉(栈区),所以list的值仍然是NULL。

错误的创建链表头方法

下图是正确的创建方法,因为这次是传的&list,形参副本情况如图所示。现在list’里面存放的不是拷贝list的值了,而是list的地址,因此pNew把地址给*list就可以达到修改list值的目的了,因为这是用指针直接对list操作。

正确的创建链表头方法

无头结点的情况

  • 无头结点的情况和有头结点的情况其实就在于头节点是不是存放有效数据,其实创建节点的时候并没有差太多。把头结点的数据域加入数据就变成无头结点了,这篇文章想说的是删除节点的情况。

删除节点

  • 因为我们假设有头结点时不能删除头结点,因此删除节点指的是无头结点的情况,要把所有节点都删去,并把头指针置空。要注意的是,删除头指针时会涉及二级指针。

  • 不使用二级指针

int main()
{
  Node *list=NULL;
  CreatNode(&list);
  traverse(list);
  del_node_head(list);
  printf("list:%d",list);
}
void del_node_head(Node *list)
{
  printf("\n------del_func start------\n");
  Node *p=list;
  if((list)->next==NULL)
  {
  	printf("list:%d ,p:%d  Head Node , Ready to Del... ",list,p);
  	free(list);
  	list=NULL;
  	printf("Del Success \nlist:%d ,p:%d",list,p);
  }
  printf("\n------del_func over------\n\n");
}

只创建了一个节点,之后进行删除,我们可以看以下程序运行结果:

Link-List Data: 0

------del_func start------
list:11801568 ,p:11801568  Head Node , Ready to Del... Del Success
list:0 ,p:11801568
------del_func over------

list:11801568

在del函数中,让p=list,即p也指向第一个节点,使用打印函数可以看到这时p和list的值是相同的,都是第一个节点的地址。然后释放了list,并将list置空,再次打印时list=0,但p的值仍是11801568,因为只是释放了这个地址的空间,并且将list置空,但是并没有将p也置空,因此p现在的情况是:它的值还是第一个节点的地址,然而这个地址指向的空间已经被释放掉了。所以如果想要用*p去访问的话,会得到随机的数据。

回到主函数,输出list的值也是11801568,这个不难理解,结合前一篇所说,在del函数里面是把11801568这篇空间给释放了,但这个地址还在且list’=0,这只是形参,不会对主函数中的list产生影响。如果想* list会输出没有意义的值,和在del函数中*p一样(当然,它们的值相等)。

也可以在主函数里加上 list=NULL,这样就不用二级指针了,但是似乎把这个功能都放在函数里更好,所以下面要讨论使用二级指针的情况。

  • 使用二级指针
   int main()
   {
   	Node *list=NULL;
   	CreatNode(&list);
   	traverse(list);
   	del_node_head(&list);
   	printf("%d",list);
   }


   void del_node_head(Node **list)
{
   printf("\n------del_func start------\n");
   Node *p=*list;
   if((*list)->next==NULL)
   {
   	printf("list:%d ,p:%d  Head Node , Ready to Del... ",*list,p);
   	free(*list);
   	*list=NULL;
   	printf("Del Success \nlist:%d ,p:%d",*list,p);
   }
   printf("\n------del_func over------\n\n");
}

可以看到使用二级指针和不使用二级指针的代码稍有不同,我们同样来看一下输出结果。

Link-List Data: 0

------del_func start------
list:7738336 ,p:7738336  Head Node , Ready to Del... Del Success
list:0 ,p:7738336
------del_func over------

0

由于del函数的参数是**list,所以传过来的list是指针的地址,因此让p=* list,这时p和* list的值相同,都是第一个节点的地址。然后释放掉* list,又将其置空,再次输出:list变为0了,但p还是7738336,原因和上一段代码相同,free(* list)是将编号为7738336的那块地址空间释放,至于这个编号,它是客观存在的,所以即便p得值还是7738336,它也空有值而已,输出* p是没有意义的值。
回到主函数,可以看到此时list的值为0了,这是因为使用了二级指针,直接对指针的值操作,也就是说在del函数里直接对主函数中的list操作,所以main中的list为0,此时 * list会报错,因为list的值都是0了(即它不指向任何地址),还想去读这个地址中的数据当然会报错。

二级指针删除第一个节点

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值