目录
1. 第一版代码
2. 第二版代码
3. 第三版代码
前文已介绍无头单向不循环链表的实现,详见下文:
【数据结构】_不带头非循环单向链表-CSDN博客
但对于部分方法如尾插、头插、任意位置前插入、任意位置前删除的相关实现,其形参均采用了二级结构体指针类型。
本文以尾插为例,分析单链表的二级指针传参方法的实现逻辑。
1. 第一版代码
当参数为一级指针时,phead用于接收实参传递的链表结点指针:
void SLTPushBack(SLTNode* phead, SLTDataType x) {
SLTNode* newNode = CreatSLTNode(x);
SLTNode* cur = phead;
while (cur->next) {
cur = cur->next;
}
cur->next = newNode;
}
考虑链表为空的情况:
当链表为空(即没有链表结点)时,头指针phead为空,用于遍历结点的指针pcur也被赋值为空,对于遍历找尾时的while (cur->next)需要对cur进行解引用,此时就会出错,故需单独对链表进行判空操作;
2. 第二版代码
void SLTPushBack(SLTNode* phead, SLTDataType x) {
SLTNode* newNode = CreatSLTNode(x);
// 空链表
if (phead == NULL) {
phead = newNode;
}
else {
// 非空链表
SLTNode* cur = phead;
while (cur->next) {
cur = cur->next;
}
cur->next = newNode;
}
}
编写测试函数:
void Test2() {
SLTNode* plist = NULL;
SLTPushBack(plist, 1);
SLTPrint(plist);
}
调用后,运行结果如下:
调试发现:当SLTPushBack执行结束后,形参的phead值按照预想被成功赋值,而实参plist并未发生改变:
这就是传值调用的问题:采用与形参同类型的形参来接收实参,在函数内可实现形参的改变,但无法改变实参。
需要改变一级结构体指针型变量plist,则需使用传址调用,故而SLTPushBack的参数除结点数据x外,另一个参数需为二级结构体指针变量:SLTNode** pphead;
3. 第三版代码
将SLTPushBack的参数变更为SLTNode** pphead后,对比以下3个变量*plist、plist、&plist如下:
(1)*plist:第一个结点;
(2)plist = *pphead:指向第一个结点的指针;
(3)&plist = pphead:指向第一个结点的指针的地址;
void SLTPushBack(SLTNode** pphead, SLTDataType x) {
assert(pphead);
SLTNode* newNode = CreatSLTNode(x);
// 空链表
if (*pphead == NULL) {
*pphead = newNode;
}
else {
// 非空链表
SLTNode* cur = *pphead;
while (cur->next) {
cur = cur->next;
}
cur->next = newNode;
}
}
同时请注意,由于需对pphead进行解引用操作,故需保证pphead不为空,加断言即可;
此时再调用测试函数,运行结果如下:
注:1、本文仅是做具体分析,在实际编写方法时仅需考虑该操作是否涉及实参值的改变,就可判断出到底需要传值还是传址。
对于单链表来说,由于测试时首先创建了一个结构体指针变量plist,令其指向单链表的头结点,并且plist会作为实参。
故而判断其方法是否需要传二级指针,只需考虑该方法是否可能导致链表的头结点发生替换(即头结点指针是否会发生变化)。
(1)对于头插、指定位置前插入、头删、尾删、指定位置前删除等方法都需使用二级指针,因为可能会令新插入的结点作为头结点,或是删除了原本是头结点的结点;
(2)对于指定位置后插入、指定位置后删除都无需使用二级指针,因为并不会导致链表头结点指针的变化。
2、关于传值调用与传址调用,在C语言的指针部分已有过相关介绍:
【C语言】_传值调用与传址调用_c语言选址调用-CSDN博客文章浏览阅读275次,点赞8次,收藏4次。1、传址调用可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数中的变量;但实参a与形参x,实参b与形参y的地址并不相同,即各自占用其内存空间,可见虽然形参x接收了实参a的值,形参y接收了实参b的值;使用tmp交换x与y并不能实现a与b的交换;_c语言选址调用https://blog.csdn.net/m0_63299495/article/details/144946598https://blog.csdn.net/m0_63299495/article/details/144946598https://blog.csdn.net/m0_63299495/article/details/144946598只是当参数类型较为复杂,尤其是实参为指针类型变量时,可能会忽略或混淆传值调用与传址调用的相关判断。