二級指標的作用詳解
一、概念
在如下的A指向B、B指向C的指向關係中:
首先
C是"一段內容",比如你用malloc或者new分配了一塊記憶體,然後塞進去"一段內容",那就是C了。C的起始地址是0x00000008。
B是一個指標變數,其中存放著C的地址,但是B也要佔空間的啊,所以B也有地址,B的起始地址是0x00000004,但是B記憶體中存放的是C的地址,所以B裡面的內容就是0x00000008。
那麼到此為止都比較好理解:
- B= 0x00000008; //B的內容
- *B = "一段內容"; //B解引用,也就是B指標指向的C的值
- &B = 0x00000004; //B取地址,B的地址是0x00000004
那麼,再來看A:
A是二級指標變數,其中存放著B的地址0x00000004,A也有地址,是0x00000000;
- *A = B= 0x00000008; //A解引用也就是B的內容
- **A = *B = "一段內容"; //B解引用,也就是B指標指向的C的值
- A = &B = 0x00000004; //A存的是B的地址,B的地址是0x00000004
- &A = 0x00000000; //A取地址
二、使用
二級指標作為函式引數的作用:在函式外部定義一個指標p,在函式內給指標賦值,函式結束後對指標p生效,那麼我們就需要二級指標。
看看下面一段程式碼:有兩個變數a,b,指標q,q指向a,我們想讓q指向b,在函式裡面實現。
1.先看看一級指標的實現
- #include<iostream>
- using namespace std;
- int a= 10;
- int b = 100;
- int *q;
- void func(int *p)
- {
- cout<<"func:&p="<<&p<<",p="<<p<<endl; //note:3
- p = &b;
- cout<<"func:&p="<<&p<<",p="<<p<<endl; //note:4
- }
- int main()
- {
- cout<<"&a="<<&a<<",&b="<<&b<<",&q="<<&q<<endl; //note:1
- q = &a;
- cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl; //note:2
- func(q);
- cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl; //note:5
- system("pause");
- return 0;
- }
這麼寫有什麼問題?為什麼*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.二級指標操作
- #include<iostream>
- using namespace std;
- int a= 10;
- int b = 100;
- int *q;
- void func(int **p) //2
- {
- cout<<"func:&p="<<&p<<",p="<<p<<endl;
- *p = &b; //3
- cout<<"func:&p="<<&p<<",p="<<p<<endl;
- }
- int main()
- {
- cout<<"&a="<<&a<<",&b="<<&b<<",&q="<<&q<<endl;
- q = &a;
- cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;
- func(&q); //1
- cout<<"*q="<<*q<<",q="<<q<<",&q="<<&q<<endl;
- system("pause");
- return 0;
- }
這裡只改了三個地方,變成傳二級指標。我們再看:
因為傳了指標q的地址(二級指標**p)到函式,所以二級指標拷貝(拷貝的是p,一級指標中拷貝的是q所以才有問題),(拷貝了指標但是指標內容也就是指標所指向的地址是不變的)所以它還是指向一級指標q(*p = q)。在這裡無論拷貝多少次,它依然指向q,那麼*p = &b;自然的就是 q = &b;了。
3.再看一個例子:
我們程式碼中以二級指標作為引數比較常見的是,定義了一個指標MyClass *ptr=NULL,在函式內對指標賦值*ptr=malloc(...),函式結束後指標依然有效.這個時候就必須要用二級指標作為引數func(MyClass **p,...),一級指標為什麼不行上面說了。
- void my_malloc(char **s)
- {
- *s=(char*)malloc(100);
- }
- void main()
- {
- char *p=NULL;
- my_malloc(&p);
- //do something
- if(p)
- free(p);
- }
這裡給指標p分配記憶體,do something,然後free(p),如果用一級指標,那麼就相當於給一個p的拷貝s分配記憶體,p依然沒分配記憶體,用二級指標之後,才對p分配了記憶體。