• 传值,传指针(地址),传引用以及表添加函数中为什么要用指向链表指针的指针(C++/链表)


    原文链接:https://www.cnblogs.com/inception6-lxc/p/8996050.html

    在看书的时候有个往链表里添加节点的函数,代码中考虑到可能给出的头指针为空,并做另外一些处理。具体代码如下:

    [cpp] view plain copy
     
     
    print?
    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. }  
    #include <iostream>
    #include <string>
    using namespace std;

    struct ListNode
    {
    int val;
    ListNode* next;
    };

    void AddToTail(ListNode** pHead, int value);

    int main() {
    // TODO
    }

    void AddToTail(ListNode** pHead, int value) {
    ListNode* pNew = new ListNode();
    pNew->val = value;
    pNew->next = NULL;

    if (*pHead == NULL) {
    	*pHead = pNew;
    }
    else {
    	ListNode* p = *pHead;
    	while (p-&gt;next != NULL) {
    		p = p-&gt;next;
    	}
    	p-&gt;next = pNew;
    }
    

    }

            网上其他人的博客中对函数AddToTail的参数的描述跟书中如出一辙:第一个参数pHead是一个指向指针的指针,当向一个空链表插入一个节点时,新插入的节点是链表的头指针,此时会改动头指针,因此必须把pHead参数设置为指向指针的指针。

            为什么呢?在以前学习C++的时候,我们只知道在参数中,以传值的形式作为参数的变量在函数体内被修改之后,出了函数体就会失效,准确的说这个变量没有被修改过,因此需要传入该变量的指针或者使用引用传参的方式。可是上述AddToTail中已经是一个指针了啊?于是我测试了一下,不使用指针的指针会怎样:

    [cpp] view plain copy
     
     
    print?
    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. }  
    #include <iostream>
    #include <string>
    using namespace std;

    struct ListNode
    {
    int val;
    ListNode* next;
    };

    void AddToTail(ListNode* pHead, int value);

    int main() {
    // TODO
    ListNode* head = NULL;
    AddToTail(head, 10);
    if (head != NULL) {
    cout << head->val << endl;
    }
    else {
    cout << "head is NULL.." << endl;
    }

    }

    void AddToTail(ListNode* pHead, int value) {
    ListNode* pNew = new ListNode();
    pNew->val = value;
    pNew->next = NULL;

    if (pHead == NULL) {
    	pHead = pNew;
    }
    else {
    	ListNode* p = pHead;
    	while (p-&gt;next != NULL) {
    		p = p-&gt;next;
    	}
    	p-&gt;next = pNew;
    }
    

    }

            运行结果如下

            作为指针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函数的第一个参数也作为引用参数传入(指向指针的指针肯定正确啦,就不测试了),此时预测的结果是修改有效。代码如下:

    [cpp] view plain copy
     
     
    print?
    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. }  
    #include <iostream>
    #include <string>
    using namespace std;

    struct ListNode
    {
    int val;
    ListNode* next;
    };

    void AddToTail(ListNode* &pHead, int value);

    int main() {
    // TODO
    ListNode* head = NULL;
    AddToTail(head, 10);
    if (head != NULL) {
    cout << head->val << endl;
    }
    else {
    cout << "head is NULL.." << endl;
    }

    }

    void AddToTail(ListNode* &pHead, int value) {
    ListNode* pNew = new ListNode();
    pNew->val = value;
    pNew->next = NULL;

    if (pHead == NULL) {
    	pHead = pNew;
    }
    else {
    	ListNode* p = pHead;
    	while (p-&gt;next != NULL) {
    		p = p-&gt;next;
    	}
    	p-&gt;next = pNew;
    }
    

    }



            只是简单的在代码2中的函数声明和定义中,第一个参数加入了"&"表示使用一个引用参数,结果如下图,check!

  • 相关阅读:
    面对缓存,有哪些问题需要思考?
    .NET 文件格式相关开源项目
    (转)谈谈用ASP.NET开发的大型网站有哪些架构方式(成本)
    (转)基于微软平台IIS/ASP.NET开发的大型网站有哪些?
    sql查询优化策略
    初入linux系统
    Npoi操作Excel
    List GroupBy真实用法,Reflection(反射)用法,Enum用法,正则,搜索下拉布局
    3.2.2.4 文本匹配锚点
    3.2.2.3 单个表达式匹配多字符
  • 原文地址:https://www.cnblogs.com/shiheyuanfang/p/13771572.html
Copyright © 2020-2023  润新知