我想應該還有不少人在使用指標上有些地方觀念不大清楚,比如說下面二個函式,那個是正確的?為什麼?像這樣的問題如果弄不清楚,寫出來的程式一定非常危險。
現在就來說明為什麼,在這之前先要了解在C/C++中,函式的參數是如何傳遞的,在C/C++中函式的呼叫所傳入的參數是透過堆疊(Stack) 來傳入函式的,不懂什麼是堆疊也沒關係,就把它看成是另外一塊記憶體也行,當在程式中呼叫某個函式時,傳入的參數會先被複製到這塊記憶體中,當在函式中要使用這些參數時再從堆疊中去取出來。
以版本1的例子來說明,如下在程式中大概會這樣呼叫。
現在來看另外一個類似例子,會比較清楚些。
// 為簡化忽略檢查如上,這個函式要配置大小是100個字元的記憶體並從傳入的參數p回傳,這二個版本除了輸入參數不一樣外大致上是一樣的;從第一個版本來看,參數是一個字元指標,記憶體配置出之後直接傳給p,如果觀念正確的人一定可以馬上指出這樣的寫法是錯誤的,第二個版本才是正確能work的。
void alloc_mem(char* p) // 版本1
{
p = new char[100];
}
void alloc_mem(char** p) // 版本2
{
*p = new char[100];
}
現在就來說明為什麼,在這之前先要了解在C/C++中,函式的參數是如何傳遞的,在C/C++中函式的呼叫所傳入的參數是透過堆疊(Stack) 來傳入函式的,不懂什麼是堆疊也沒關係,就把它看成是另外一塊記憶體也行,當在程式中呼叫某個函式時,傳入的參數會先被複製到這塊記憶體中,當在函式中要使用這些參數時再從堆疊中去取出來。
以版本1的例子來說明,如下在程式中大概會這樣呼叫。
char* pp = NULL;pp一開始的初值是NULL,當呼叫alloc_mem時,pp的值會被複製到堆疊中(傳址),這種情況和以下的code事實上是對等的,只不過 p的值一開始被初始化成和pp的值一樣,p就好像一個區域變數一樣,一離開函式後這個變數就無效了,所以在外面的pp的值永遠都不會改變,同時在涵式中 new出來的記憶體也lost掉了。
alloc_mem(pp);
void alloc_mem()再來看版本2,它的參數是一個指標的指標,這是什麼意思,我們先從實際使用上來看,如下。
{
char* p = new char[100];
}
char* pp = NULL;這次我們把pp這個變數的位址傳入涵式,所以在涵式中所得到的是pp這個變數的位址,在函式中p所含的內容是pp的位址,pp是一個char*形態的變數,p是一個指標它的內容是char*的形態,現在p已經指向pp了,所以對p的內容作改變,相對的pp的值也會跟著改變。
alloc_mem(&pp);
現在來看另外一個類似例子,會比較清楚些。
void change_val(int* i)這個例子和上面是一樣的,只不過變數的形態從char*改成了int,仔細去對照比較一下,回頭再去看alloc_mem相信能更容易明白。
{
*i = 5;
}
int ii = 3;
change_val(&ii);
搞懂指標才算學會C
回覆刪除不過指標的指標的確很容易搞錯…
所以有些會寫
typedef (char*) LPDATA;
就是用指標看起來就會「像」用其他變數一樣
void alloc(LPDATA* p);
...
LPDATA p;
alloc(&p);
話說,雙重指標還算常用,但在我的印象之中,好像沒有用過
三重指標,好像也想不出什麼實際運用XD
這讓我想到template的template的template,以前可能會玩玩,甚至直接用在案子裡。不過,我現在會儘可能避免寫出像這種"正常人"看不懂的程式,愈簡單愈好。
回覆刪除Essential C++ ch6 用另一種寫法:
回覆刪除void alloc_mem(char* &p) // 版本3
{
p = new char[100];
}
char* pp = NULL;
alloc_mem(pp);