函數形參爲指向鏈表頭指針的指針

一、函數的形參爲指向鏈表頭指針的指針。當頭指針爲空時,需要使用指向指針的指針。

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]  傳值,傳指針(地址),傳引用以及表添加函數中爲什麼要用指向鏈表指針的指針

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章