C++ 指標與引用 知識點 小結

來源:互聯網
上載者:User

標籤:指標   引用   數組   控制代碼   new   

【摘要】

指標可以指向變數、數組、字串、函數、甚至結構體。即指標可以指向不同資料對象。指標問題 包括 常量指標、數組指標、函數指標、this指標、指標傳值、指向指標的指標 等。主要知識點包括:1.指標與引用在概念上的三個區別及其在const、sizeof、自增運算上的差異;2.熟記經典swap函數的指標實現與引用實現,並能反映輸出錯誤的兩個函數的思想弊端;3.熟記GetMem函數的錯誤形式以及錯誤產生的原因;4.比較數組、指標與靜態變數作為函數返回值的差異;5.str、*str以及&str三者的關係;6.指標繼承複指向中虛函數、實函數及變數間的關係;7.寫出const指標,指向const的指標,指向const的const指標;8.分析高維數組指標與高維數組在取址上的差別;9.區分懸浮指標與null 指標;10.new和malloc的常見問題,本質區別,為什麼產生new,為什麼不提出malloc;11.this指標3個點,為什麼會有this,this什麼時候用,this怎麼傳給函數的;12.控制代碼和智能指標。

【本文】

考點:指標與引用的區別

  • 指標需要進行合法性檢測而引用不需要;
  • 指標可以賦值重新指向新的對象(不是地址、不是類型),但是引用只能指向初始化被指定的對象不能改變;
  • 指向一個對象且指向的對象不改變時應使用引用,若存在不指向任何對象或指向的對象存在改變時應採用指標;
  • 指標可以指向空值,但是引用不可以指向空值。因此,程式員可能有指向空值的時候,即允許變數為空白的時候,應該使用指標;
  • 引用的代碼效率要高於指標(具體體現在於能否在初始化時候不賦值)。
【補充】
1)引用沒有const,指標有const,這裡需要切記,使用const聲明的同時必須要初始化;
2)指標指向一塊記憶體,它的內容是所指向存的地址,引用時某塊記憶體的別名;
3)sizeof 引用 得到的是所指向的變數的大小;sizeof 指標得到的是指標本身的大小;
4)指標和引用的自增運算意義不一樣。
【例】
int &ref; // 錯誤,引用不可為空且需要同時初始化,因此錯誤!int *p = 5; // 錯誤,指標位置向實際記憶體空間,賦值之後不知道存放的地址,沒有指向,因此錯誤!int *ptra,*ptrb;int ptra = *ptrb;// 錯誤,指標位置向實際記憶體空間,賦值之後不知道存放的地址,因此錯誤!const int num;// 錯誤,const 常量賦值必須同時初始化,因此錯誤!
考點:經典的 swap(int &a,int &b)函數
指標方式:傳入的是地址,接收的是指標,處理的是改變指標的指向關係;
引用方式:傳入的是變數(即,swap(int a,int b)),接收的是引用(地址,即 swap(int &ref_a,int&ref_b)),處理的是改變引用的指向關係,也就是引用變數本身;

誤區
  • 採用變地址方式交換最終會釋放改變成果;
  • 函數若採用指標變數做介質會導致記憶體泄露;

考點:指標申請記憶體空間
函數源碼
void GetMemA(char *p,int num){p = (char*)malloc(sizeof(char)*num);}void GetMemB(char **p,int num){*p = (char*)malloc(sizeof(char)*num);}

【解析】

因為,函數不能傳值只能傳址,所以,在函數內採用指標申請記憶體空間(即,void GetMem(char *p,int num))是不會成功的。那麼為實現空間申請我們應該採用指向指標的指標(即,void GetMem(char **p,int num))。

假設,在 main() 函數中,變數 p 地址為 1 指向地址為 2(可以想象成變數 p 值為 2),*p 地址為 2 指向地址為 3(可以想象成變數 *p 值為 3),**p 地址為 3 存放一 char 型變數。

在 void GetMemA(char *p,int num)中,暫時稱函數變數為 GetMemA.p 吧,很明顯 GetMemA.p 是一個地址,指向一個 char 型變數。函數申請棧區進入函數後,GetMemA.p 地址為 4 ,指標變數擷取一塊長度為 num 的 char 型記憶體空間。函數調用結束,彈棧銷毀 變數GetMemA.p 及其申請的空間,那麼,主函數中的變數 p 沒有得到任何改變;

在 void GetMemB(char **p,int num)中,暫時稱函數變數為 GetMemB.*p 吧,很明顯 GetMemB.*p 是一個地址,指向一個 char 型變數。函數申請棧區進入函數後,GetMemB.*p 地址為 5 ,指標變數擷取一塊長度為 num 的 char 型記憶體空間。函數調用結束,彈棧銷毀 變數 GetMemB.p 及其申請的空間(注意,這裡是變數 GetMemB.p 而不是 變數 GetMemB.*p),那麼,GetMemB.*p 自然而然的保留了申請得到的記憶體空間,主函數中的變數 p 沒有得到任何改變,而 *p 獲得了一段長度為 num 的 char 型記憶體空間;

當然,函數還可寫成這樣直接返回記憶體空間。

char* GetMemC(char *p,int num){p = (char*)malloc(sizeof(char)*num);}
【整型資料的源碼傳遞】
#include <iostream>using namespace std;void GetMemory2(int *z){    *z=5;};int main(){    int v;    GetMemory2(&v);    cout << v << endl;    return 0;}
【注】
總之,改變的是函數中元素指向的變數就能有效改變變數的數值,如果改變的是函數中元素本身,不論元素是地址還是別的,都會在函數調用結束後被釋放掉。

字串的關鍵知識點
區分數組字串和指標字串
char *strA(){    char str[] = "hello world";    return str;}
解析:
這個str裡存在的地址是函數strA棧裡“hello world”的首地址。函數調用完成,棧幀恢複調用strA之前的狀態,臨時空間被重設,堆棧“回縮”,strA棧幀不再屬於應該訪問的範圍。這段程式可以正確輸出結果,但是這種存取方法違背了函數的棧幀機制。只要另外一個函數調用的話,你就會發現,這種方式的不合理及危險性。【未理解這句話。】
——後記
OS認為這段記憶體是可以被使用的,該段記憶體內容的改變或者其他動作會使得該段記憶體不再被 str 正確調用。
如果想獲得正確的函數,改成下面這樣就可以:
char *strA(){    char *str = "hello world";    return str;}
首先要搞清楚char *str 和 char str[] :
char str[] = "hello world";是分配一個局部數組。局部數組是局部變數,它所對應的是記憶體中的棧。局部變數的生命週期結束後該變數不存在了。
char *str = "hello world";是指向了常量區的字串,位於靜態儲存區,它在程式生命期內恒定不變,所以字串還在。無論什麼時候調用 strA,它返回的始終是同一個“唯讀”的記憶體塊。
它返回的始終是同一個“唯讀”的記憶體塊。重要的事情說三遍!它返回的始終是同一個“唯讀”的記憶體塊。
另外想要修改上述字元陣列變數的不合理分配,也可以這樣:
char *strA(){    static char str[] = "hello world";    return str;}
通過static開闢一段靜態存貯空間。
答案:
因為這個函數返回的是局部變數的地址,當調用這個函數後,這個局部變數str就釋放了,所以返回的結果是不確定的且不安全,隨時都有被收回的可能。
【總之,返回值不能是數組,最好是指標!】


【另】

char *str = “hello”;
// 那麼:*str = “h”;str = “hello”;&str = (地址)
【另】
char *a[ ] = { "hello","the","world"};
char **ptra = a;
// ptra++; cout<< *ptra << endl; // 輸出 為 the
// cout<< *ptra++ << endl; // 輸出 為 hello
【換言之,‘*’的優先順序高於‘++’】
字串 和 數組的理解分析 詳見 深入理解 字串 和 數組;
MSBD.V.4.0 - P.69.3 ~ 70.5 ~ 72.7 ~ 73.9- IMPT !!!

記憶體位移
記憶體中兩個指標 ptra 和 ptrb,其中,(ptra - ptrb)的值不是實際地址的數學差。(假設 ptra 和 ptrb 指向整型變數)實質上它們在編譯其中的運算為(ptra - ptrb)/sizeof(int)。
位移地址,程式碼範例 01
#include ...class A{ ... };class B{ ... };int main( ){      A a;      B *pb = (B*)(&a);}
【解析】
這裡將對象 a 的地址賦給了一個指向 B 類對象的指標,這樣的賦值是很野蠻的。雖然,不一定報錯,造成的後果是強制把 a 地址內容看成是一個 B 類對象,pb 指向的是 a 類的記憶體空間。
賦值和繼承 導致 成員變數數值傳遞 的 比較分析 
詳見:C++ 類繼承與對象賦值 情況下 成員變數的覆蓋 淺析
詳址:http://blog.csdn.net/u013630349/article/details/46722893
位移地址,程式碼範例 02:
#include ...int main( ){      int *ptr;      ptr = (int*)0x8000;      *ptr = oxaabb;}
【解析】
代碼的運行將會報錯。這段代碼的本意是將 0x8000 記憶體空間 指向一個 整型資料的地址。其中,0x8000 指向的空間儲存 oxaa ,0x8001 指向的空間儲存 oxbb 。指標隨機分配地址是不被允許的,除非所有的資源為程式員操作,否則,盡量避免操作底層。
位移地址,程式碼範例 03:
#include<iostream>using namespace std;struct S{int i;int *p;}main( ){S s;int *p = &s.i;p[0] = 1;p[1] = 5;cout<<"s 的地址 \t"<<&s<<endl;cout<<"s.i 的地址 \t"<<&s.i<<endl;cout<<"p 的值 \t\t"<<p<<endl;cout<<"p[0] 的地址 \t"<<&p[0]<<endl;cout<<"s.p 的地址 \t"<<&s.p<<endl;cout<<"p[1] 的地址 \t"<<&p[1]<<endl;cout<<"s.i 的值 \t"<<s.i<<endl;cout<<"s.p 的值 \t"<<s.p<<endl;cout<<"*s.p 的值 \t"<<"報錯"<<endl;cout<<"s.*p 的值 \t"<<"報錯"<<endl;cout<<"&s.*p 的值 \t"<<"報錯"<<endl;s.p = p;cout<<"************************"<<endl;cout<<"執行s.p = p; \t"<<"之後"<<endl;cout<<"************************"<<endl;cout<<"s 的地址 \t"<<&s<<endl;cout<<"s.i 的地址 \t"<<&s.i<<endl;cout<<"p 的值 \t\t"<<p<<endl;cout<<"p[0] 的地址 \t"<<&p[0]<<endl;cout<<"s.p 的值 \t"<<s.p<<endl;cout<<"s.p 的地址 \t"<<&s.p<<endl;cout<<"s.p[0] 的值 \t"<<s.p[0]<<endl;cout<<"s.p[1] 的值 \t"<<s.p[1]<<endl;cout<<"p[1] 的地址 \t"<<&p[1]<<endl;cout<<"p[0] 的值 \t"<<p[0]<<endl;cout<<"p[1] 的值 \t"<<p[1]<<endl;s.p[1] = 1;cout<<"************************"<<endl;cout<<"執行s.p[1] = 1;\t"<<"之後"<<endl;cout<<"************************"<<endl;cout<<"s.p = "<<s.p<<endl;cout<<"s.p+1 = "<<s.p+1<<endl;//cout<<"*s.p = "<<*s.p<<endl; // 崩潰}
【代碼輸出】
s 的地址        0018FF40s.i 的地址      0018FF40p 的值           0018FF40p[0] 的地址   0018FF40s.p 的地址     0018FF44p[1] 的地址   0018FF44s.i 的值         1s.p 的值        00000005*s.p 的值     報錯s.*p 的值      報錯&s.*p 的值   報錯************************執行s.p = p;    之後************************s 的地址        0018FF40s.i 的地址      0018FF40p 的值          0018FF40p[0] 的地址     0018FF40s.p 的值        0018FF40s.p 的地址      0018FF44s.p[0] 的值     1s.p[1] 的值     1638208p[1] 的地址     0018FF44p[0] 的值       1p[1] 的值       1638208************************執行s.p[1] = 1; 之後************************s.p = 00000001s.p+1 = 00000005Press any key to continue
【程式碼分析】
int *p = &s.i;// 等價於 p = &p[0] = &s = &s.i// 且還有 p+1 = &p[1] = &s+1 = &s.i+1 = &s.ps.p = p;// 要知道 (s.*p) = s.p = p// 等價有 *(p+1) = *&p[1] = *&s+1 = *&s.i+1 = *&s.p = p// 簡化得 *(p+1) = p[1] = s+1 = s.i+1 = s.p = p// 已知道 p = &p[0] = s.p = &s.p[0]// 所以有 &p[0] = p[1] 和 &s.p[0] = s.p[1]s.p[1] = 1// 此時再給,s.p[0]或者p[0]賦值,等價於在記憶體位址為 1 的空間寫入變數。// 所以,此句暫時不會報錯,一旦對指向地址賦值,就會導致程式崩潰。
考點:各種類型的指標表示
優先順序 ()> 資料類型(如: int 、 char etc.)> [ ] > * > ++
函數指標 void (*ptr)();
函數返回指標 int *func();
const 指標 const int *ptr;
指向 const 的指標 int *const ptr;
指向 const 的 const 指標 const int *const ptr;
一個有10個指標的數組,指標指向一個函數,該函數有一個整形參數 並返回一個整型
int ( *ptr ( int ))[10]
int (*(*ptr)(int,int))(int)
ptr是一個輸入參數為兩個整型變數的函數指標,其返回值是一個輸入參數為一個整形變數返回值為整型的的函數指標


函數指標聲明方式
...
int max(int,int);// 已有求最大值函數 
...

int *ptr(int,int) = &max;

詳見:

C++ 高維指標數組 與 高維數組指標(一)

C++ 高維指標數組 與 高維數組指標(二)

C++ 數組指標 指標數組 以及 函數指標 指標函數

詳址:

http://blog.csdn.net/u013630349/article/details/44998689

http://blog.csdn.net/u013630349/article/details/44195523

http://blog.csdn.net/u013630349/article/details/45098899


【程式碼範例】

int a[] = {1,2,3,4,5};

int *ptr = (int*)(&a+1);           

printf("%d %d", *(a+1), *(ptr-1));

說明:*(a+1) 直接就為2 簡單&a+1   由於本身a就為數組名也就是指標,加上& 相當於雙指標 也就相當於**(a+1) 所以加1 就是數組整體加一行,ptr指向a的第6個元素,所以*(ptr-1)為5

詳見:C++ 數組指標 指標數組 以及 函數指標 指標函數

詳址:http://blog.csdn.net/u013630349/article/details/45098899


考點:null 指標和懸浮指標
null 指標為申請了記憶體空間,但是,指向為 NULL 或者 0 的指標,即 ptr = 0 ,此時,對指標操作雖然會導致崩潰,但,調試起來比懸浮指標方便許多;

懸浮指標為申請了記憶體,經過一系列操作,釋放記憶體後的指標。指標雖不再指向固定記憶體,但是,指標還是存在,且指向隨機地區,此時對指標操作十分危險的。


考點:malloc/free 與 new/delete

malloc free new delete 的區別、比較、分析 

詳見:C++ 中 malloc/free 與 new/delete 淺析

詳址:http://blog.csdn.net/u013630349/article/details/44947255


考點:指標與控制代碼
Windows採用控制代碼標記系統資源,隱藏系統資訊。你只需要知道有這個東西,然後去調用就好了,控制代碼是一個32位無符號整型數。
指標則是標記某個實體記憶體地址。

auto_ptr
std::auto_ptr <Object> pObj(new Object);
auto_ptr 好處在於在析構時會自動刪除此指標,但是不要誤用;
1)不能共用所有權,即不要讓兩個auto_ptr指向同一個對象;
2)不能指向數組,因為在析構時候調用delete,而不是delete[];
3)不能作為容器的對象;

考點:this 指標

詳見:C++ this 指標 淺析

詳址:http://blog.csdn.net/u013630349/article/details/46412485

C++的編譯系統只用了一段空間來存放在公用函數代碼,在調用各個對象的成員函數時,都要調用這個公用的函數代碼
this指標的值為當前調用成員函數的對象的起始地址。
一種情況就是,在類的非靜態成員函數中返回類對象本身的時候,直接使用 return *this;另外一種情況是當參數與成員變數名相同時使用this指標,如this->num = num (不能寫成num = num)。
this指標只能在成員函數中使用。全域函數,靜態函數都不能使用this。 成員函數預設第一個參數為 T* const this。
this是通過函數的首參來傳遞的。this指標是在調用之前產生的。類執行個體後的函數,沒有這個說法。類在執行個體化時,只分配類中的變數空間,並沒有為函數分配空間,類的函數在定義完成後,它就在那兒,不會跑的。   

遺留問題:
【野指標】
控制代碼
智能指標

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

C++ 指標與引用 知識點 小結

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.