2009年12月19日 星期六

關於C/C++的指標

我想應該還有不少人在使用指標上有些地方觀念不大清楚,比如說下面二個函式,那個是正確的?為什麼?像這樣的問題如果弄不清楚,寫出來的程式一定非常危險。
// 為簡化忽略檢查
void alloc_mem(char* p) // 版本1
{
  p = new char[100];
}

void alloc_mem(char** p) // 版本2
{
  *p = new char[100];
}
如上,這個函式要配置大小是100個字元的記憶體並從傳入的參數p回傳,這二個版本除了輸入參數不一樣外大致上是一樣的;從第一個版本來看,參數是一個字元指標,記憶體配置出之後直接傳給p,如果觀念正確的人一定可以馬上指出這樣的寫法是錯誤的,第二個版本才是正確能work的。

現在就來說明為什麼,在這之前先要了解在C/C++中,函式的參數是如何傳遞的,在C/C++中函式的呼叫所傳入的參數是透過堆疊(Stack) 來傳入函式的,不懂什麼是堆疊也沒關係,就把它看成是另外一塊記憶體也行,當在程式中呼叫某個函式時,傳入的參數會先被複製到這塊記憶體中,當在函式中要使用這些參數時再從堆疊中去取出來。

以版本1的例子來說明,如下在程式中大概會這樣呼叫。
char* pp = NULL;
alloc_mem(pp);
pp一開始的初值是NULL,當呼叫alloc_mem時,pp的值會被複製到堆疊中(傳址),這種情況和以下的code事實上是對等的,只不過 p的值一開始被初始化成和pp的值一樣,p就好像一個區域變數一樣,一離開函式後這個變數就無效了,所以在外面的pp的值永遠都不會改變,同時在涵式中 new出來的記憶體也lost掉了。
void alloc_mem()
{
  char* p = new char[100];
}
再來看版本2,它的參數是一個指標的指標,這是什麼意思,我們先從實際使用上來看,如下。
char* pp = NULL;
alloc_mem(&pp);
這次我們把pp這個變數的位址傳入涵式,所以在涵式中所得到的是pp這個變數的位址,在函式中p所含的內容是pp的位址,pp是一個char*形態的變數,p是一個指標它的內容是char*的形態,現在p已經指向pp了,所以對p的內容作改變,相對的pp的值也會跟著改變。

現在來看另外一個類似例子,會比較清楚些。
void change_val(int* i)
{
  *i = 5;
}

int ii = 3;
change_val(&ii);
這個例子和上面是一樣的,只不過變數的形態從char*改成了int,仔細去對照比較一下,回頭再去看alloc_mem相信能更容易明白。

3 則留言:

  1. 搞懂指標才算學會C
    不過指標的指標的確很容易搞錯…
    所以有些會寫
    typedef (char*) LPDATA;
    就是用指標看起來就會「像」用其他變數一樣

    void alloc(LPDATA* p);
    ...
    LPDATA p;
    alloc(&p);

    話說,雙重指標還算常用,但在我的印象之中,好像沒有用過
    三重指標,好像也想不出什麼實際運用XD

    回覆刪除
  2. 這讓我想到template的template的template,以前可能會玩玩,甚至直接用在案子裡。不過,我現在會儘可能避免寫出像這種"正常人"看不懂的程式,愈簡單愈好。

    回覆刪除
  3. Essential C++ ch6 用另一種寫法:

    void alloc_mem(char* &p) // 版本3
    {
    p = new char[100];
    }

    char* pp = NULL;
    alloc_mem(pp);

    回覆刪除

Related Posts Plugin for WordPress, Blogger...