1. 程式人生 > >二級指標的作用詳解

二級指標的作用詳解

一、概念

在如下的A指向B、B指向C的指向關係中:

首先

C是"一段內容",比如你用malloc或者new分配了一塊記憶體,然後塞進去"一段內容",那就是C了。C的起始地址是0x00000008。

B是一個指標變數,其中存放著C的地址,但是B也要佔空間的啊,所以B也有地址,B的起始地址是0x00000004,但是B記憶體中存放的是C的地址,所以B裡面的內容就是0x00000008。

那麼到此為止都比較好理解:

  1. B= 0x00000008;  //B的內容   
  2. *B = "一段內容";  //B解引用,也就是B指標指向的C的值  
  3. &B = 0x00000004;  //B取地址,B的地址是0x00000004  

那麼,再來看A:

A是二級指標變數,其中存放著B的地址0x00000004,A也有地址,是0x00000000;

  1. *A = B= 0x00000008;  //A解引用也就是B的內容   
  2. **A = *B = "一段內容";  //B解引用,也就是B指標指向的C的值  
  3. A = &B = 0x00000004;  //A存的是B的地址,B的地址是0x00000004  
  4. &A = 0x00000000;  //A取地址  

二、使用

二級指標作為函式引數的作用:在函式外部定義一個指標p,在函式內給指標賦值,函式結束後對指標p生效,那麼我們就需要二級指標。

看看下面一段程式碼:有兩個變數a,b,指標q,q指向a,我們想讓q指向b,在函式裡面實現。

1.先看看一級指標的實現

  1. #include<iostream>  
  2. using namespace std;  
  3. int a= 10;  
  4. int b = 100;  
  5. int *q;  
  6. void func(int *p)  
  7. {  
  8.     cout<<"func:&p="<<&p<<",p="<<p<<endl;  //note:3  
  9.     p = &b;  
  10.     cout<<"func:&p="<<&p<<",p="<<p<<endl;  //note:4  
  11. }  
  12. int main()  
  13. {  
  14.     cout<<"&a="<<&a<<",&b="<<&b<<",&q="<<&q<<endl;  //note:1  
  15.     q = &a;  
  16.     cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;  //note:2  
  17.     func(q);  
  18.     cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;  //note:5  
  19.     system("pause");  
  20.     return 0;  
  21. }  

這麼寫有什麼問題?為什麼*q不等於100?我們看一下輸出便知:

&a=0032F000,&b=0032F004,&q=0032F228 *q=10,q=0032F000,&q=0032F228 func:&p=0018FD24,p=0032F000 func:&p=0018FD24,p=0032F004 *q=10,q=0032F000,&q=0032F228

我們看輸出:

note:1->a,b,q都有一個地址.

note:2->q指向a.

note:3->我們發現引數p的地址變了,跟q不一樣了,是的引數傳遞是製作了一個副本,也就是p和q不是同一個指標,但是指向的地址0x0032F000(a的地址)還是不變的.

note:4->p重新指向b.

note:5->退出函式,p的修改並不會對q造成影響。

結論:

編譯器總是要為函式的每個引數製作臨時副本,指標引數p的副本是 p,編譯器使 p = q(但是&p != &q,也就是他們並不在同一塊記憶體地址,只是他們的內容一樣,都是a的地址)。如果函式體內的程式修改了p的內容(比如在這裡它指向b)。在本例中,p申請了新的記憶體,只是把 p所指的記憶體地址改變了(變成了b的地址,但是q指向的記憶體地址沒有影響),所以在這裡並不影響函式外的指標q。

這就需要二級指標操作:

2.二級指標操作

  1. #include<iostream>  
  2. using namespace std;  
  3. int a= 10;  
  4. int b = 100;  
  5. int *q;  
  6. void func(int **p)  //2  
  7. {  
  8.     cout<<"func:&p="<<&p<<",p="<<p<<endl;  
  9.     *p = &b;  //3  
  10.     cout<<"func:&p="<<&p<<",p="<<p<<endl;  
  11. }  
  12. int main()  
  13. {  
  14.     cout<<"&a="<<&a<<",&b="<<&b<<",&q="<<&q<<endl;  
  15.     q = &a;  
  16.     cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;  
  17.     func(&q);  //1  
  18.     cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;  
  19.     system("pause");  
  20.     return 0;  
  21. }  

這裡只改了三個地方,變成傳二級指標。我們再看:

因為傳了指標q的地址(二級指標**p)到函式,所以二級指標拷貝(拷貝的是p,一級指標中拷貝的是q所以才有問題),(拷貝了指標但是指標內容也就是指標所指向的地址是不變的)所以它還是指向一級指標q(*p = q)。在這裡無論拷貝多少次,它依然指向q,那麼*p = &b;自然的就是 q = &b;了。

3.再看一個例子:

我們程式碼中以二級指標作為引數比較常見的是,定義了一個指標MyClass *ptr=NULL,在函式內對指標賦值*ptr=malloc(...),函式結束後指標依然有效.這個時候就必須要用二級指標作為引數func(MyClass **p,...),一級指標為什麼不行上面說了。

  1. void  my_malloc(char **s)    
  2. {    
  3.     *s=(char*)malloc(100);    
  4. }    
  5. void  main()    
  6. {    
  7.     char  *p=NULL;    
  8.     my_malloc(&p);  
  9.     //do something  
  10.     if(p)  
  11.         free(p);    
  12. }    

這裡給指標p分配記憶體,do something,然後free(p),如果用一級指標,那麼就相當於給一個p的拷貝s分配記憶體,p依然沒分配記憶體,用二級指標之後,才對p分配了記憶體。