(转发)链表添加函数中为什么要用指向链表指针的指针(引用传参)
https://blog.csdn.net/shen_jz2012/article/details/50631317
在看书的时候有个往链表里添加节点的函数,代码中考虑到可能给出的头指针为空,并做另外一些处理。具体代码如下
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 struct ListNode 6 { 7 int val; 8 ListNode* next; 9 }; 10 11 void AddToTail(ListNode** pHead, int value); 12 13 int main() { 14 // TODO 15 } 16 17 void AddToTail(ListNode** pHead, int value) { 18 ListNode* pNew = new ListNode(); 19 pNew->val = value; 20 pNew->next = NULL; 21 22 if (*pHead == NULL) { 23 *pHead = pNew; 24 } 25 else { 26 ListNode* p = *pHead; 27 while (p->next != NULL) { 28 p = p->next; 29 } 30 p->next = pNew; 31 } 32 }
网上其他人的博客中对函数AddToTail的参数的描述跟书中如出一辙:第一个参数pHead是一个指向指针的指针,当向一个空链表插入一个节点时,新插入的节点是链表的头指针,此时会改动头指针,因此必须把pHead参数设置为指向指针的指针。
为什么呢?在以前学习C++的时候,我们只知道在参数中,以传值的形式作为参数的变量在函数体内被修改之后,出了函数体就会失效,准确的说这个变量没有被修改过,因此需要传入该变量的指针或者使用引用传参的方式。可是上述AddToTail中已经是一个指针了啊?于是我测试了一下,不使用指针的指针会怎样:
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 struct ListNode 6 { 7 int val; 8 ListNode* next; 9 }; 10 11 void AddToTail(ListNode* pHead, int value); 12 13 int main() { 14 // TODO 15 ListNode* head = NULL; 16 AddToTail(head, 10); 17 if (head != NULL) { 18 cout << head->val << endl; 19 } 20 else { 21 cout << "head is NULL.." << endl; 22 } 23 24 } 25 26 void AddToTail(ListNode* pHead, int value) { 27 ListNode* pNew = new ListNode(); 28 pNew->val = value; 29 pNew->next = NULL; 30 31 if (pHead == NULL) { 32 pHead = pNew; 33 } 34 else { 35 ListNode* p = pHead; 36 while (p->next != NULL) { 37 p = p->next; 38 } 39 p->next = pNew; 40 } 41 }
作为指针pHead竟然真的没被修改过!
其实真的很好理解,既然你懂得函数中的值传参,假设int a,作为参数传入的时候没被修改,所以需要用指向a的指针,那么应该也可以理解,指针变量pHead作为参数传入的时候被修改无效,因此需要用指向pHead的指针,只不过pHead本身就是一个指针了,所以才存在有指针的指针看起来稍微复杂一点的说法。因为,指向a的指针作为参数传入进去时,如果你对它进行修改,其实也是无效的,但是修改指针指向的内容的修改是有效的,也即,(&a)对a取地址得到的指针传入进去之后,此时你修改这个指针也是没有什么实际作用的,原因我等下会说。但是,你修改指针指向的内容这就有效了,因此通常我们在函数体内是修改对指针取内容后的内存,即*(&a)。所以,你对指针pHead的修改时无效的,只有对指向pHead的指针指向的内容(很绕吧,其实就是pHead),这时候才是有效的,因此AddToTail的第一个参数必须用指针的指针。
现在来说说为什么对值传参在函数体内的修改无效。因为a传进去的时候会被复制了一份copy,此后的修改都是在临时变量copy上,出了函数体copy被销毁,a还是原来的a,根本就没被修改过,所以才会值传参对变量的修改无效。要使得对a的修改有效,一方面是传入a的地址,也就是对指向a的指针作为值传参(反正修改的不是a的指针,修改了也无所谓,反正只是修改a的指针的copy),此时a的指针的copy指向的内容也是a,因此对copy指向的内容修改会导致a的内容也被修改,check!另外一种方式就是引用传参,引用传参往往要比值传参高效,因为它是直接将a作为参数传入进去,而少了对a进行复制这部分的开销,既然传入进去的是a,那么对a的修改肯定也生效。
为了证明上述废话,我将代码2中的AddToTail函数的第一个参数也作为引用参数传入(指向指针的指针肯定正确啦,就不测试了),此时预测的结果是修改有效。代码如下:
----------------
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 struct ListNode 6 { 7 int val; 8 ListNode* next; 9 }; 10 11 void AddToTail(ListNode* &pHead, int value); 12 13 int main() { 14 // TODO 15 ListNode* head = NULL; 16 AddToTail(head, 10); 17 if (head != NULL) { 18 cout << head->val << endl; 19 } 20 else { 21 cout << "head is NULL.." << endl; 22 } 23 24 } 25 26 void AddToTail(ListNode* &pHead, int value) { 27 ListNode* pNew = new ListNode(); 28 pNew->val = value; 29 pNew->next = NULL; 30 31 if (pHead == NULL) { 32 pHead = pNew; 33 } 34 else { 35 ListNode* p = pHead; 36 while (p->next != NULL) { 37 p = p->next; 38 } 39 p->next = pNew; 40 } 41 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix