一、函數的形參爲指向鏈表頭指針的指針。當頭指針爲空時,需要使用指向指針的指針。
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] 傳值,傳指針(地址),傳引用以及表添加函數中爲什麼要用指向鏈表指針的指針