c语言指针和多重指针

数据结构是一种思想,不限制于某一种语言,但再各种语言实现中却有许多小细节不可忽视,比如c语言中的指针,是个让人十分头疼的问题。

先以链表为例,这里定义了链表的结点表示,data为该结点存储的数据,next是指向该节点的数据,
注:下文的所有代码都只是简化的代码,并不能上机运行。

typedef struct node
{
	Element data;
	struct node *next;
}Node;

这是链表一个结点的样子
在这里插入图片描述

为防止C初学者迷惑,做个例子
Node a -->int b; 这里Node就是类型,声明一个Node结构体a,跟声明整型变量b是一样的。

Node *c -->int *d; c是指向Node结构体的指针,d是指向整型数据的指针。他们的本质也是一样的。

在链表的操作中,有对指针的初始化,清空链表,增加和删除链表等等,接下来我们分两种情况讨论,第一种是没有头节点,第二种是有头节点

1: 没有头节点的单链表中,即第一个结点就存储数据的链表,如下图所示是有四个结点的无头结点单链表。
在这里插入图片描述
下面演示无头节点空链表的添加结点操作,在实际中没有意义,但很好理解c数据结构指针的问题。

//第一段代码
void Add(int num,Node *list2)
{	
	list2 = (Node *)malloc(sizeof(Node));
}
int main()
{	
	Node *list = NULL;
	Add(1,list);
	return 0;
}

第一段代码是错误的操作,下面是正确的

//第二段代码
void Add(int data,Node **list2)
{
	*list2 = (Node *)malloc(sizeof(Node));
}
int main()
{
	Node *list = NULL;
	Add(1,&list);
	return 0;
}

因为第一段代码在main函数中声明了指向Node的指针,想通过调用Add函数使list指向新的结点,但实际程序是这样做的,在Addt中临时创建指向Node的指针list2,把list的值赋给了list2,然后把list2指向新节结点,然后Addt函数执行完,list2变量被销毁,list原封不动,还是指向NULL。

而第二段代码中,把list的地址传递给InitList函数,在InitList中创建了指向指针的指针list2,即list2指向一个指针,这个指针是指向Node结构体的指针。即list2 = &list,然后又执行*list2 = (Node *)malloc(Node),因为指针list2的值就是指针list的地址,即list = (Node *)malloc(Node)。实参list的值原先指向NULL,现指向新结点。
这种现象在无头节点的链表的许多操作中都会出现,比如头插法,清空链表,初始化链表等等

接下来看有头节点的链表添加操作,即头节点不存储数据的单链表,如下图所示是有四个结点的有头结点单链表,四个节点中,三个是存放真实数据,头节点的数据域也不是没用,一般用来存放链表个数。

在这里插入图片描述
下面的代码是有头节点链表的添加操作

//第三段代码
void Add(int data,Node *list2)
{	
	list2->next = (Node *)malloc(sizeof(Node));
	list2->data ++;
}
int main()
{
	Node *list = (Node *)malloc(sizeof(Node));
	list->data = 0;//链表数据域是有效数据的结点个数
	Add(1,list);
	//执行完Add(list)后,list->data已变为1;
	return 0;
}

可能会以为第三段代码是错误的,添加不上代码,其实是正确的,程序在Add函数中声明了指向结构体Node的指针list2,并且list赋值给list2,即list和list2都指向了同一个结点
在这里插入图片描述
然后执行了 list2->next = (Node *)malloc(sizeof(Node));即通过形参list2改变了实参list所指向的Node结构体中指针null的值,list的值并未发生改变,也与第一段代码失败的结论相照应。执行完函数,list2被摧毁,而链表已完成添加结点的操作。

可以看出当没有头节点时,在空链表中添加元素,改动的是头指针本身的值,所以需要在函数中传递指针的指针,而有头结点时,改变的是头指针所指向结构体的值。从而发现有头节点的链表很大程度上简化了无头结点链表的操作,使很多需要单独判断的情况消失。

树和图也有类似需要注意的指针现象,因为树结构没有头节点等等原因。

补充:如果想在没有头节点的链表的操作中不使用双重指针的函数,可以用下面的思想,在函数中返回指针,并在main函数中用头指针list接收

Node *Add(int data,Node *list2)
{
	//添加结点的代码
	return list2;
}

int main()
{
	Node *list = NULL;
	list = Add(1);
	list = Add(2);
	return 0;
}

c的指针是真的有魅力,java实现的数据结构都看不到好多细节。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值