关于C语言实现单链表中的指针问题
导言
笔者最近学习严薇敏教授的《数据结构》,同时也参考B站上王道考研的数据结构视频,发现单链表传入函数参数这一方面教材与视频,与网络上的一些实现写法各有不同,有点迷惑。经过查了一些资料以后,终于拨云见日。写下这篇文章,希望为后来的同学们有所帮助。水平不足,还请多多指正!
指针
指针是指向内存中一定大小内存区域的变量。定义一个指针如:
int *p;
但这样只是声明了一个指针变量p,p指向哪里我们不清楚,需要指定这个指针变量指向的内存区域
int x = 2; int *p = NULL; //初始化,避免p是野指针 p = &x; //让p指向x
指针变量实际上存放的是目标区域的内存地址。
我们要注意区分*p = 和 p =这两种情况
*p = 2; //让p指向的内存区域中的值变成2 p = &a; //让指针p指向内存中的区域a,也就是让p的值为a的地址
注意:指针变量的值是地址,取内容符号与指针变量搭配,改变的不是指针的值,而是指针指向的内存区域的值
函数中参数传递问题
C语言中,参数传递有两种,一个是值传递,一个是地址传递。值传递情况下,C程序只是获得了值,也就是变量的拷贝,在C程序内对变量的任何操作都不会引起内存中原本的变量的变化。想要改变传递进来的参数,就需要进行地址传递。
void test(int *a){ *a = 2; } int main(){ int x = 0; test(&a); //传入变量的地址 printf("%d\n",x); //输出结果为2 return 0; }
链表中指针的使用(带头结点为例)
我们在单链表中,用到了指针来实现单链表
typedef struct LNode{ int data; struct LNode *next; }LNode, *LinkList; LinkList L;
怎么理解这个L?
我们要知道,LinkList本质上是一个指针,它指向的是内存中大小为的LNode类型区域。那么,L这个指针,存储的应该是目标区域的地址。
一开始我们只是声明了这么一个指针L,还没有将它指向一片内存区域。为了将它指向一片内存区域,我们需要使用malloc函数来分配内存空间
L = (LNode *)malloc(sizeof(LNode)); //注意要用(LNode *)或者(LinkList)
//来对malloc函数返回的地址进行类型转换 if (L == NULL) return false;
这个时候我们就要注意,这个对单链表进行初始化的函数initList,接收的参数应该是怎么样的?
经过验证,我们得知,如果直接传入指针L,那么我们将不能更改内存中L的指向区域。为了改变指针L,我们有两种方案可以选择:
-
通过return语句来返回地址
LinkList initList(LinkList L){ L = (LinkList)malloc(sizeof(LNode)); if (L == NULL) return NULL; else{ L->next = NULL; //设置L指向的区域(头结点)的next域的值 return L; } } int main(){ LinkList L; L = initList(L); return 0; }
-
使用二级指针,来对一级指针L的值(指向的内存区域是哪一片)进行改变。
int initList(LinkList *L){ //此处的L是二级指针,指向的是:指向头结点的头指针 *L = (LinkList)malloc(sizeof(LNode));
//*L就是二级指针的内容,也就是一级指针存储的值,也就是目标区域地址。所以,可以直接把*L当头指针用 if (*L == NULL) return 0; else{ (*L)->next = NULL; return 1; } } int main(){ LinkList L; initList(&L); return 0; }
必须使用二级指针的操作
根据单链表的基本操作,我们总结一下必须要用二级指针的情况,实际上也就是对单链表内容进行更改的情况:
-
initList 链表初始化
-
listInsert 向指定位置插入
-
listDelete 删除指定位置元素
-
-
getElem 获取具体某个元素的结点(这个看情况,如果只是找到链表中第几个元素的值,那么就没必要用二级指针)