函数形参为指向指针的指针

一、函数的形参为指向指针的指针。当链表头指针为空时,需要使用指向指针的指针。

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 nullptr\n";
	cout << "-------------------------------\n";
	cout << "The res of InsertNode02:  ";
	InsertNode02(p2);
	PrintVal(p2);
	cout << "p2的地址为: " << p2 << endl;
	if (p2 == nullptr)
		cout << "p2 is nullptr\n";
	
	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]  传值,传指针(地址),传引用以及表添加函数中为什么要用指向链表指针的指针

发布了213 篇原创文章 · 获赞 48 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/Jeffxu_lib/article/details/102763161
今日推荐