以前在學習資料結構的時候一直沒弄懂函數參數裡面傳遞 ** p 以及使用 &的含義,這裡摘抄了一小段文章方便理解。不懂的時候再看看這幾段代碼。
指標參數是如何傳遞記憶體的?
如果函數的參數是一個指標,不要指望用該指標去申請動態記憶體。見如下例子:
void GetMemory(char *ip, int num)
{
ip = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char *str = NULL;
GetMemory(str, 100); // str 仍然為 NULL
strcpy(str, "hello"); // 運行錯誤
}
試圖用指標參數申請動態記憶體
毛病出在函數GetMemory中。編譯器總是要為函數的每個參數製作臨時副本,指標參數ip的副本是 _ip,編譯器使 _ip = ip.如果函數體內的程式修改了_ip的內容,就導致參數ip的內容作相應的修改。這就是指標可以用作輸出參數的原因。在本例中,_ip申請了新的記憶體,只是把_ip所指的記憶體位址改變了,但是ip絲毫未變。所以函數GetMemory並不能輸出任何東西。事實上,每執行一次GetMemory就會泄露一塊記憶體,因為沒有用free釋放記憶體。
如果非得要用指標參數去申請記憶體,那麼應該改用“指向指標的指標”,見如下樣本:
void GetMemory(char **p, int num) { *p = (char *)malloc(sizeof(char) * num); } void Test(void) { char *str = NULL; GetMemory(&str, 100); // 注意參數是 &str,而不是str strcpy(str, "hello"); std::cout<< str << std::endl; free(str); } |
這裡寫一段自己的理解:這個程式是想通過函數給str指標分配一點記憶體--malloc,然後再給str指標賦值。。。注意這是在另一個函數內部改變str'指標的指向(str原來是指向NULL的,現在要使之指向函數GetMemory內分配的記憶體地區)所以要使用二級指標來實現,其實質就是改變str指標的指向(比如從 指向A 改為指向B),而不是改變str指標指向地址的內容哦。
用指向指標的指標申請動態記憶體
當然,我們也可以用函數傳回值來傳遞動態記憶體。這種方法更加簡單,見如下樣本:
char *GetMemory(int num) { char *ip = (char *)malloc(sizeof(char) * num); return ip; } void Test(void) { char *str = NULL; str = GetMemory(100); strcpy(str, "hello"); std::cout<< str << std::endl; free(str); } |
我們在做鏈表的時候,我們肯定希望在用一個函數creatLink(...)函數來增加鏈表節點。那麼我們可以有2種方法來實現
第一種,用一級指標
typedef struct node{ ... ... }list; node *create(list *l){ list *head; head = l; malloc...//為節點申請記憶體空間 ... ... //操作 return head; } int main(...){ ... ... list *listhead createList(listhead); .... //以後的任何操作,我們都要考慮,我們是否拿到的是鏈表頭指標,到底哪個是鏈表波的頭指標,我們是否要renturn下來返回鏈表頭指標??等。。。。 }
這樣做可以達到刪除增加節點的目的,但是,在任何情況下,我們的操作都得死死地抓住頭指標,也即是我們增加刪除節點後,任何對鏈表長度的修改,我們都要 鏈表頭指標返回,即 return head;所以,我們要通過這個函數最後獲得頭指標,抓住他,死死地抓住他,然後操作。
第二種方法:用雙指標,也即是二級指標。
typedef struct node{ ... ... }list; void create(list **l){ list *head; head = *l; //這裡不知道寫成 *l 對不對,原文是寫的l,我自己覺得應該是 *l malloc...//為節點申請記憶體空間 ... ... //操作 } int main(...){ ... ... list *st createList(st) .... .. //以後的任何操作,不管是刪除還 是插入,我們不需要考慮,我們是否已經return head了,不需要,我們在任何情況下,對鏈表的操作都只需要使用st來完成,因為,st就是鏈表的頭指標,不變,因為在申明st的時候,已經為st分配 一個地址空間,它是存在的,一直存在,直到main函數結束 }
總結: 如果函數參數中傳遞的是指標,而且想改變傳入指標的指向的話,則可以使用 二級指標來實現。(當然如果不在函數中的情況則可以直接給一級指標重新賦值使之指向另一個資料)