1. 程式人生 > >(C++)函數參數傳遞中的一級指針和二級指針

(C++)函數參數傳遞中的一級指針和二級指針

nbsp alt 得到 要求 processor func realloc 釋放內存 運行

(C++)函數參數傳遞中的一級指針和二級指針

主要內容:

1、一級指針和二級指針

2、函數指針傳遞的例子

3、什麽時候需要傳遞二級指針?

4、二級指針在鏈表中的使用

1、一級指針和二級指針

一級指針:即我們一般說的指針,就是內存地址;

二級指針:指向指針的指針,就是地址的地址;

如:

int a=1;

int *p=&a;  // p為a變量的地址,通過*p可以得到a的值

int **q=&p;   // q為p指針的地址,通過**q可以得到a的值

2、函數指針傳遞的例子

程序1:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include<stdio.h>
void fun(int *p){ int b=100; p=&b; } int main(){ int a=10; int *q; q=&a; printf("%d\n",*q); fun(q); printf("%d\n",*q); return 0; }

運行結果:

10

10

程序2:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include<stdio.h> void fun(int **p){
int b=100; *p=&b; } int main(){ int a=10; int *q; q=&a; printf("%d\n",*q); fun(&q); printf("%d\n",*q); return 0; }

運行結果:

10

100

程序3:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include<stdio.h> #include<stdlib.h> void myMalloc(
char *s){ s=(char*)malloc(100); } int main() { char *p=NULL; myMalloc(p); if(p==NULL) printf("P is not changed!\n"); else{ printf("P has been changed!\n"); free(p); } return 0; }

運行結果:

P is not changed!

程序4:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include<stdio.h> #include<stdlib.h> void myMalloc(char **s){ *s=(char*)malloc(100); } int main() { char *p=NULL; myMalloc(&p); if(p==NULL) printf("P is not changed!\n"); else{ printf("P has been changed!\n"); free(p); } return 0; }

運行結果:

P has been changed!

3、什麽時候需要傳遞二級指針?

通過上述例子,我們可以看到,在某些情況下,函數參數傳遞一級指針時,在函數體內對指針做變動,也不會對原始指針產生變化,而傳遞二級指針時,則可以,這是為什麽呢?

在傳遞一級指針時,只有對指針所指向的內存變量做操作才是有效的;

在傳遞二級指針時,只有對指針的指向做改變才是有效的;

下面做簡單的分析:

在函數傳遞參數時,編譯器總會為每個函數參數制作一個副本,即拷貝;

例如:

void fun(int *p),指針參數p的副本為_p,編譯器使_p=p,_p和p指向相同的內存空間,如果在函數內修改了_p所指向的內容,就會導致p的內容也做相應的改變;

但如果在函數內_p申請了新的內存空間或者指向其他內存空間,則_p指向了新的內存空間,而p依舊指向原來的內存空間,因此函數返回後p還是原來的p。

這樣的話,不但沒有實現功能,反而每次都申請新的內存空間,而又得不到釋放,因為沒有將該內存空間的地址傳遞出來,容易造成內存泄露。

void fun(int **p),如果函數參數是指針的地址,則可以通過該參數p將新分配或新指向的內存地址傳遞出來,這樣就實現了有效的指針操作。

如果覺得二級指針比較難理解,也可以通過函數返回值的形式來傳遞動態內存(切記不能返回棧內存),如:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include<stdio.h> #include<stdlib.h> char* myMalloc(){ char *s=(char*)malloc(100); return s; } int main() { char *p=NULL; p=myMalloc(); if(p==NULL) printf("P is not changed!\n"); else{ printf("P has been changed\n"); free(p); } return 0; }

知道了上述這些,就不難理解上面四個小程序的執行結果了。

4、二級指針在鏈表中的使用

在鏈表或者樹的操作中,也需要用到二級指針,

比如創建鏈表的頭指針:

在初始化鏈表函數中,傳入頭指針,並在函數中為該指針分配空間,此時就應該使用二級指針,如void initLinklist(Node **head);

而在添加刪除結點的過程中,我們並沒有改變函數參數指針的指向,而是通過傳入的指針如Node *head,找到要刪除結點的位置,並未對該指針做改變,因此退出函數後,該指針無影響。

(1) c++中的懸浮指針:聲明了但沒有被付值的指針,它指向內存中的任意一個空間。避免懸浮指針的一個方法是開始就付值為NULL

(2)“野指針”不是NULL指針,是指向“垃圾”內存的指針。人們一般不會錯用NULL指針,因為用if語句很容易判斷。但是“野指針”是很危險的,if語句對它不起作用。野指針的成因主要有兩種:

一、指針變量沒有被初始化。任何指針變量剛被創建時不會自動成為NULL指針,它的缺省值是隨機的,它會亂指一氣。所以,指針變量在創建的同時應當被初始化,要麽將指針設置為NULL,要麽讓它指向合法的內存。
二、指針p被free或者delete之後,沒有置為NULL,讓人誤以為p是個合法的指針。別看free和delete的名字惡狠狠的(尤其是delete),它們只是把指針所指的內存給釋放掉,但並沒有把指針本身幹掉。通常會用語句if (p != NULL)進行防錯處理。很遺憾,此時if語句起不到防錯作用,因為即便p不是NULL指針,它也不指向合法的內存塊。例:
  char *p = (char *) malloc(100);
  strcpy(p, “hello”);
  free(p); // p 所指的內存被釋放,但是p所指的地址仍然不變
  if(p != NULL) // 沒有起到防錯作用
  strcpy(p, “world”); // 出錯  

三、另外一個要註意的問題:不要返回指向棧內存的指針或引用,因為棧內存在函數結束時會被釋放。strlen是對char*的,string不行,這個很容易讓人誤解啊

四、我們易犯的錯誤:

對於第二個錯誤很容易在C++中出現,比如在類的定義時的構造函數和析構函數,如果在構造函數中動態開辟(new),在析構函數中要釋放,然而我們一般都delete釋放內存後就結束了,殊不知,指向先前內存的指針就成了野指針(迷途指針),稍有不慎,就會出錯,當你向未知區域賦值時,運氣好的話會是程序運行錯誤,要是運氣不佳,很可能引起系統崩潰!
解決方法:將將要指向未知區域的指針(剛定義的或是釋放內存的指針)等於NULL或指向常量,使用指針之前再做判斷null

無論在什麽情況下delete之後是否要設置為NULL?唯一的判斷標準就是以後會不會再用它, 如果以後有可能用,就一定設置為NULL,否則就不必, 除非是對軟件的性能要求很強,否則盡管每次delete後都設置NULL好了 這樣做,永遠是不會運行出錯的, 潛在的後果就是,這個賦NULL值的操作,浪費了你萬分之一秒不到的時間。

五 、知識補充:

一般我們常說的內存泄漏是指堆內存的泄漏。堆內存是指程序從堆中分配的,大小任意的(內存塊的大小可以在程序運行期決定),使用完後必須顯示釋放的內存。應用程序一般使用malloc,realloc,new等函數從堆中分配到一塊內存,使用完後,程序必須負責相應的調用free或delete釋放該內存塊,否則,這塊內存就不能被再次使用,我們就說這塊內存泄漏了

(C++)函數參數傳遞中的一級指針和二級指針