一級指針形式如: int *p
二級指針形式如: int **p
可能很多初學者會疑惑在函數參數里面到底應該用一級指針還是二級指針。
例子:
第一個是鏈表
一個鏈表結構:
typedef struct Node
{
int value;
struct Node *next;
}
對于添加數據到鏈表尾部這個函數,傳入的參數必須是一個二級指針。
在傳參的時候發生的賦值動作是:
Node *hNode;
Node **pNode = &hNode;
pNode指向了hNode的地址,因此*pNode的使用效果相當于操作hNode的值。
可以改變hNode的指向。
那如果參數用一級指針會有什么效果。
此時在傳參的時候發生的賦值動作是:
Node *hNode;
Node *pNode = hNode;
形參pNode此時和hNode都指向同一塊內存(這塊內存可能是NULL)。
此時添加一個新的結點到鏈表尾部,而恰好此時鏈表為NULL。
添加后形參pNode會指向一塊新的內存,而此時hNode仍然是指向原來的那塊內存(即NULL)。
在函數返回的時候,hNode仍是指向NULL,形參pNode作為局部變量將會自動銷毀,而它所指向的那塊內存因為是動態分配的
所以沒有被非局部指針指向或被釋放,因此發生內存泄漏了。
所以這里必須要用二級指針。
再舉一個常見例子。
定義一個函數,給一個整形指針(初始為NULL)分配指定的內存。
錯誤做法:
void mallocMem(int *p,int size)
{
p = new int[size];
}
許多人會以為函數形參為指針意思就是把原來的指針直接傳進去函數中使用,其實即使形參是指針,也會發生復制。
在上面談及鏈表的時候我也說過,假如我們在main函數要調用mallocMem傳遞一個指針 int *s; 那么在函數調用處會發生一個賦值動作,
p = s; 所以事實上 p 和 s 是兩個指針,只不過它們指向的內存地址是一樣的。
p = new int[size]后 p就相當于拋棄了 s ,而指向了一個新的內存,在函數返回時,s還是指向原來的地方(NULL). 而且還會發生內存泄漏。
正確做法:
void mallocMem(int **p,int size)
{
*p = new int[size]; //這里s也指向了新的內存地址。
}
必須用二級指針的另外幾個鏈表操作函數還有:刪除結點 。
所以凡是涉及到會造成指針所指向地方會發生內存動態分配的操作(new delete),都必須用二級指針才能保證安全。
而剩下的就是不必用二級指針的鏈表函數(遍歷,合并等等)。
簡單來說:一級指針能做到的事情二級指針都能做到,反之不然。
大家在使用的時候要好好想想到底要對這個指針做什么操作。
文章列表