# 為何一個指標實作 add_entry() 會出事 ## (上) 前情提要 * 我發現我的程式會崩潰,甚至連第一個 entry 都加不進去,以下是我將 pointer to pointer 改寫為指標的程式片段 : ```cpp= void add_entry(node_t *h, int new_value) { /*create new_node*/ //printf(Address of h = %p, &h) if(h == NULL) h = new_node; else { node_t *tmp = h; while (tmp) { tmp = tmp->next; } tmp = new_node; } } ... int main() { node_t *head = NULL; add_entry(head, 7); //printf(address of head = %p, &head); add_entry(head, 13); ... return 0; } ``` * 我故意將原本老師函式中的 `head` 改成 `h`,為了強調與主程式中的 `head` 是不同的變數。 ## (下) 釐清問題的本質! ### 1. 函式的傳值 (pass by value) 與傳址 (pass by reference) * 一個變數其實有兩個屬性 : **值**與**位址**,若透過傳值方式傳遞變數,則僅僅只會把變數的**值**複製一份到函式中給另一個變數,不論函式裡面做了甚麼事,都不會影響原本的變數,如下程式碼: ```cpp= void add(int a) { a++; } int main() { int a = 0; add(a); printf("%d\n", a); // still 0, right? } ``` * 那想在函式裡面能夠改變原本的變數,則透過傳址的方式,函式會透過指標來接住原本變數的位址,如此一來這個指標所指的內容(也就是值)便是真真正正原本變數的**值**,而我就可以在函式裡透過**取值運算**(也就是 `*` 符號,和宣告變數的 `*` 功能不同!)來更改原變數的值 : ```cpp= void add(int *a) { (*a)++; } int main() { int a = 0; add(&a); printf("%d\n", a); // will be 1, yes! } ``` ### 2. 儘管函式裡面把 `h` 指向 `new_node`,但我的 `head` 仍然指向最一開始的 `NULL` 我一個重大的發現是我誤以為上面 add_entry 程式碼中第 5 行,我把`h = new_node` 之後就以為我的 head 會更新,但實際上並不會! 雖然我已經是傳指標給函式了,看似是傳位址過去了,怎麼還是沒有更新呢?因為我其實把一些簡單的概念搞混了 ![](https://i.imgur.com/pAkCBhX.jpg) 圖的上區塊是還沒 `h = new_node`,可以看到 `head`,`h` 其實是兩個不同的變數(可以觀察程式碼第 4 及第 20 行的註解拿掉後的輸出),他們指向相同位址的確是用傳址的方式,也因此我**確實可以更改所指的內容**,但 `h = new_node` 不是改內容,是改 `h` 指的位址,改 `h` 指的位址,改 `h` 指的位址!!!**不是改它們共同指到的位址的內容** 因此這樣做根本就沒有修改到我的 `head` 對吧!(搭配圖便有更深理解),所以才衍伸到我必須把 `h` 丟出來給 `head` 才會對的結果,就只是先給 `h` 再給 `head` 而已罷了! ### 3. 為何需要 pointer to pointer * 到這裡就可以解釋為甚麼需要用到 pointer to pointer,上面的問題是 `head` 與 `h` 所指內容不同步所導致。`indirect` 是一個指標但它的內容(值)還是一個指標(也就是 `indirect` 指向 `head` 這個指標):指標 `h` 則是跟 `head` 指同樣位址而已。 * 也就是說我能夠用 `indirect` 追蹤 head 指到哪裡(對 indirect 取值便能得到 head 的位址,儘管 head 指向不同位址)