2014-02-13 40 views
0

考慮以下代碼:C++路過Refrence或值指針

#include <iostream> 
using namespace std; 

struct Node 
{ 
    int x; 
    Node *next; 
}; 

void append(Node* h1, Node* h2) 
{ 

    cout << h1 << ":" << h2; 
    cout << endl; 

    Node *ptr; 
    ptr = h1; 

    while (ptr->next != NULL) 
    { 
     ptr = ptr->next; 
    } 

    ptr->next = h2; 
} 

void insertAtHead(Node* head, int k) 
{ 

    Node *tmp = new Node; 
    tmp -> x = k; 
    tmp -> next = head; 

    head = tmp; 
} 

int main() 
{ 
    Node *n1 = new Node; 
    n1->x = 1; 
    n1->next = NULL; 


    Node *n2 = new Node; 
    n2->x = 2; 
    n2->next = NULL; 

    Node *n3 = new Node; 
    n3->x = 3; 
    n3->next = n2; 

    cout << n1 << ":" << n3; 
    cout << endl; 

    append(n1,n3); 

    insertAtHead(n1,4); 

    while(n1 != NULL) 
    { 
     cout << n1->x; 
     n1 = n1->next; 
    } 
    cout << endl; 
} 

的附加功能的工程,即使我們有Node* h1但即使我們有相同的Node* head.爲什麼insertAtHead不起作用?

我們需要Node* &head爲appendAtHead工作。爲什麼?

回答

1

在功能上追加指針H1不改變

在功能insertAtHead頭應該改變,但不是一個地方variableis改變。您應該考慮到參數是函數的局部變量。所以退出函數後,所有局部變量都被銷燬。

如果函數追加處理空列表,您可能會得到相同的效果。在這種情況下,您必須在功能內部分配頭部,這些頭部變化不會影響原始頭部。

有三種方法可以正確編寫這些功能。每個函數都返回更新的頭部,或者頭部必須通過引用或間接通過頭部指針傳遞。

例如,如果列表爲空,即第一個參數等於NULL,函數append將如何工作。

void append(Node* h1, Node* h2) 
{ 

    cout << h1 << ":" << h2; 
    cout << endl; 

    if (h1 == NULL) 
    { 
     h1 = h2; 
    } 
    else 
    { 
     Node *ptr = h1; 

     while (ptr->next != NULL) ptr = ptr->next; 

     ptr->next = h2; 
    } 
} 

正如你在這種情況下看到的那樣,它將和函數insertAtHead具有相同的錯誤。函數內部h1的變化不會影響頭部的原始值。只有局部變量h1會被改變,然後在退出函數後被銷燬。原始可變頭將保持與以前相同的舊值。

1

通過值傳遞意味着該函數將創建該參數的副本,以便它不會更改在傳遞引用時傳遞的實際參數,這樣您可以修改該函數中的參數。知道這一點,這就是爲什麼Node* &head可以工作,但Node* head不適用於insertAtHead()功能。

對於append()函數,它正在被修改,因爲Node* ptr變量指向h1的地址,該地址可讓您將下一個值設置爲h2(參數設置爲n3),而無需將參數設置爲通過參考。在n1到達append函數之前,它將下一個節點設置爲NULL,而在調用append函數之後,它現在將下一個節點設置爲n3。 (IMO我認爲你應該給他們不同的名字,它就會有種困惑看到N1,H1,N2,H2等)

+0

所以副本正在以追加改性但我們仍然看到修改後的結果。爲什麼? –

1

原因很簡單:

在這種情況下無效insertAtHead( Node * head,int k),當您按值傳遞頭時,您正在處理實際頭的副本,複製堆棧中的現有副本,以便在退出該函數時將其丟棄。

這就是您必須通過引用傳遞的原因,因此head不是堆棧中節點的副本,而是您想要執行的實際節點。

編輯回答您的評論:

追加的作品,因爲你得到的指針的拷貝,這樣你就可以修改它指向下方的數據,但你不能改變你得到了實際的指針,因爲它是一個生活在堆棧上的臨時副本。

+0

爲什麼它與append一起工作呢?不是附加副本? –

0

指針通過值傳遞(您傳遞指針值的副本 - 即指向的內存地址的副本)。

引用通過引用傳遞(即,函數參數在函數簽名中具有&前綴時)。在這種情況下,如果您將參考點指向函數內部的其他參考點,那麼當程序流程退出函數作用域並返回時,原始引用也指向另一件事。

這裏的Java的差異的圖示(使用C++由於能夠通過引用的Java不能做傳遞一個示例):

http://blog.aaronshaw.net/2014/02/13/java-is-always-pass-by-value/