概述
一、函数的形参为指向指针的指针。当链表头指针为空时,需要使用指向指针的指针。
1、形参与实参间通过地址传递
(1)、当头指针为空时,向该链表中添加新的节点,此时需要使用到指针的指针。InsertNode01 函数使用了指针的指针,所以其输出的结果显示以 p1为头指针的链表被成功添加了新元素。而 InsertNode02 函数则只是使用了指针,所以其结果只是相当于拷贝了数据,原来的链表并没有改变,依旧为空链表。
#include<iostream>
using namespace std;
struct ListNode
{
int val;
ListNode *next;
ListNode(int x = 0) :val(x), next(nullptr) {}
};
// 形参与实参之间通过地址传递
void InsertNode01(ListNode** pHead)
{
ListNode *pNewNode = new ListNode();
pNewNode->val = 88;
pNewNode->next = nullptr;
*pHead = pNewNode;
}
void InsertNode02(ListNode* pHead)
{
ListNode *pNewNode = new ListNode();
pNewNode->val = 88;
pNewNode->next = nullptr;
pHead = pNewNode;
}
void PrintVal(ListNode *pHead)
{
ListNode* p = pHead;
while (p!=nullptr)
{
cout << p->val << " ";
p = p->next;
}
cout << endl;
}
int main()
{
// 当头节点为空时
ListNode *p1 = nullptr, *p2 = nullptr;
cout << "The res of InsertNode01: ";
InsertNode01(&p1);
PrintVal(p1);
cout << "p1的地址为: " << p1 << endl;
if (p1 == nullptr)
cout << "p1 is nullptrn";
cout << "-------------------------------n";
cout << "The res of InsertNode02: ";
InsertNode02(p2);
PrintVal(p2);
cout << "p2的地址为: " << p2 << endl;
if (p2 == nullptr)
cout << "p2 is nullptrn";
cin.get();
}
运行结果如下:
(2)、分析:
第一个参数pHead是一个指向指针的指针,当向一个空链表插入一个节点时,新插入的节点是链表的头节点。此时原本是空指针的pHead 将指向新插入的头节点,并将 pHead 作为头指针。这样就会改动头指针,因此必须把 pHead 参数设置为指向指针的指针。否则会类似于值传递的形式,传入函数中的只是 pHead 的一个拷贝,在该函数中 pHead 将会指向新插入的节点,来作为头指针。但是当该插入节点的函数调用结束时,pHead 将仍然是空指针,而并未得到修改。
在C++中以值传递的方式来向一个函数传入参数,当该函数调用结束后,原来传入的值并没有改变,只是将原来传入的值进行拷贝后,在函数中进行操作,调用结束后对拷贝的值进行销毁,原来的值没有任何改变。而引用传递和地址传递则是直接对传入的参数进行修改,函数调用结束后,原来的值也会被改变。其中指针变量作为函数参数时,实参和形参之间的传递属于地址传递。
如函数 f1 (int *num),形参是地址传递,函数 f2 (int num) 是值传递。同理 ,函数 InsertNode01(ListNode** pHead) 形参是地址传递,因为pHead 本身是指针类型( ListNode* )变量,所以对其进行地址传递时要加上指针,然后再结合原来自带的指针,就成为指针的指针 ListNode** pHead,num本身是整型( int )变量,其地址传递就是 int *num;而 InsertNode02(ListNode* pHead)是值传递。
2、形参与实参通过引用进行传递
#include<iostream>
using namespace std;
struct ListNode
{
int val;
ListNode *next;
ListNode(int x = 0) :val(x), next(nullptr) {}
};
// 头指针的引用类型,pHead是指针类型,&pHead为其指针类型的引用
void InsertNode(ListNode *&pHead)
{
ListNode *pNode = new ListNode();
pNode->val = 88;
pNode->next = nullptr;
if (pHead == nullptr)
{
pHead = pNode;
}
else
{
ListNode *p = pHead;
while (p->next!=nullptr)
{
p = p->next;
}
p->next = pNode;
}
}
void PrintVal(ListNode *pHead)
{
ListNode* p = pHead;
while (p!=nullptr)
{
cout << p->val << " ";
p = p->next;
}
cout << endl;
}
int main()
{
// 当头节点为空时
ListNode *p1 = nullptr, *p2 = nullptr;
InsertNode(p1);
PrintVal(p1);
cin.get();
}
结果如下
对头指针进行引用传递也能够实现当头指针为空时,让其成为链表的头指针。
二、头指针不为空,无需改变链表头指针时
当头指针不为空时,向链表末尾添加节点,就不需要使用指针的指针来进行操作
#include<iostream>
using namespace std;
struct ListNode
{
int val;
ListNode *next;
ListNode(int x = 0) :val(x), next(nullptr) {}
};
// 创建链表,并返回链表的头指针
ListNode* CreateList()
{
ListNode *pHead = new ListNode();
ListNode *pNode = new ListNode();
pHead->val = 70, pNode->val = 80;
pHead->next = pNode;
pNode->next = nullptr;
return pHead;
}
// 在pHead不是空指针的情况下
void InsertNode03(ListNode *pHead)
{
ListNode *pNode = new ListNode();
pNode->val = 90;
pNode->next = nullptr;
ListNode *p = pHead;
while (p->next!=nullptr)
{
p = p->next;
}
p->next = pNode;
}
void PrintVal(ListNode *pHead)
{
ListNode* p = pHead;
while (p!=nullptr)
{
cout << p->val << " -----> ";
p = p->next;
}
cout << "nullptr" << endl;
}
int main()
{
// 当头指针不是空指针时
ListNode *p1 = CreateList();
InsertNode03(p1);
PrintVal(p1);
cin.get();
}
三、总结
在链表的头指针有可能为 nullptr 的情况下,若要修改链表,如向其中添加新节点时,要将链表的头指针为空的情况考虑在内。此时需要将修改链表的函数形参设置为指针的指针、或者指针的引用。
如:分别在非空的头指针和空的头指针的链表结尾添加新的节点,
#include<iostream>
using namespace std;
struct ListNode
{
int val;
ListNode *next;
ListNode(int x = 0) :val(x), next(nullptr) {}
};
// 创建链表,并返回链表的头指针
ListNode* CreateList()
{
ListNode *pHead = new ListNode();
ListNode *pNode = new ListNode();
pHead->val = 70, pNode->val = 80;
pHead->next = pNode;
pNode->next = nullptr;
return pHead;
}
void AddNode(ListNode **pHead)
{
ListNode *pNode = new ListNode();
pNode->val = 90;
pNode->next = nullptr;
if (*pHead == nullptr) //当链表的头指针为空时
{
*pHead = pNode;
}
else
{
ListNode *p = *pHead;
while (p->next!=nullptr)
{
p = p->next;
}
p->next = pNode;
}
}
void PrintVal(ListNode *pHead)
{
ListNode* p = pHead;
while (p!=nullptr)
{
cout << p->val << " -----> ";
p = p->next;
}
cout << "nullptr" << endl;
}
int main()
{
ListNode *p1 = CreateList(); // p1为非空指针
ListNode *p2 = nullptr; // p2 为空指针
// 当链表的头指针不为空时,向其末尾添加节点
AddNode(&p1);
// 当链表的头指针为空时,向其末尾添加节点(即让该指针指向新创建的节点)
AddNode(&p2);
PrintVal(p1);
PrintVal(p2);
cin.get();
}
运行结果如下,在头指针为空的情况下,让其指向新创建的节点。
参考资料
[1] 传值,传指针(地址),传引用以及表添加函数中为什么要用指向链表指针的指针
最后
以上就是怕孤单大炮为你收集整理的函数形参为指向指针的指针的全部内容,希望文章能够帮你解决函数形参为指向指针的指针所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复