c++指針 指針入門

這是一篇我所見過的關於指針的最好的入門級文章,它可以使初學者在很短的時間內掌握複雜的指針操做。雖然,如今的Java、C#等語言已經取消了指針,但做爲一個C++程序員,指針的直接操做內存,在數據操做方面有着速度快,節約內存等優勢,還是不少C++程序員的最愛。指針就像是一把良劍,就看你怎麼去應用它! 什麼是指針?  其實指針就像是其它變量同樣,所不一樣的是通常的變量包含的是實際的真實的數據,而指針是一個指示器,它告訴程序在內存的哪塊區域能夠找到數據。這是一個很是重要的概念,有不少程序和算法都是圍繞指針而設計的,如鏈表。開始學習  如何定義一個指針呢?就像你定義一個其它變量同樣,只不過你要在指針名字前加上一個星號。咱們來看一個例子:  下面這個程序定義了兩個指針,它們都是指向整型數據。int* pNumberOne;int* pNumberTwo;  你注意到在兩個變量名前的「p」前綴了嗎?這是程序員一般在定義指針時的一個習慣,以提升便程序的閱讀性,表示這是個指針。如今讓咱們來初始化這兩個指針: pNumberOne = &some_number;pNumberTwo = &some_other_number;   &號讀做「什麼的地址」,它表示返回的是變量在內存中的地址而不是變量自己的值。在這個例子中,pNumberOne 等於some_number的地址,因此如今pNumberOne指向some_number。 若是如今咱們在程序中要用到some_number,咱們就可使用pNumberOne。咱們來學習一個例子:  在這個例子中你將學到不少,若是你對指針的概念一點都不瞭解,我建議你多看幾遍這個例子,指針是個很複雜的東西,但你會很快掌握它的。  這個例子用以加強你對上面所介紹內容的瞭解。它是用C編寫的(注:原英文版是用C寫的代碼,譯者從新用C++改寫寫了全部代碼,並在DEV C++ 和VC++中編譯經過!)#include <iostream.h>void main(){// 聲明變量:int nNumber;int *pPointer;// 如今給它們賦值:nNumber = 15;pPointer = &nNumber;//打印出變量nNumber的值:cout<<"nNumber is equal to :"<< nNumber<<endl;// 如今經過指針改變nNumber的值:*pPointer = 25;//證實nNumber已經被上面的程序改變//從新打印出nNumber的值: cout<<"nNumber is equal to :"<<nNumber<<endl; }  通讀一下這個程序,編譯並運行它,務必明白它是怎樣工做的。若是你完成了,準備好,開始下一小節。陷井!  試一下,你能找出下面這段程序的錯誤嗎?#include <iostream.h>int *pPointer;void SomeFunction();{int nNumber;nNumber = 25;//讓指針指向nNumber:pPointer = &nNumber;}void main(){SomeFunction(); //爲pPointer賦值//爲何這裏失敗了?爲何沒有獲得25cout<<"Value of *pPointer: "<<*pPointer<<endl;}  這段程序先調用了SomeFunction函數,建立了個叫nNumber的變量,接着讓指針pPointer指向了它。但是問題出在哪兒呢?當函數結束後,nNumber被刪掉了,由於這一個局部變量。局部變量在定義它的函數執行完後都會被系統自動刪掉。也就是說當SomeFunction 函數返回主函數main()時,這個變量已經被刪掉,但pPointer還指着變量曾經用過的但如今已不屬於這個程序的區域。若是你還不明白,你能夠再讀讀這個程序,注意它的局部變量和全局變量,這些概念都很是重要。  但這個問題怎麼解決呢?答案是動態分配技術。注意這在C和C++中是不一樣的。因爲大多數程序員都是用C++,因此我用到的是C++中經常使用的稱謂。動態分配  動態分配是指針的關鍵技術。它是用來在沒必要定義變量的狀況下分配內存和讓指針去指向它們。儘管這麼說可能會讓你迷惑,其實它真的很簡單。下面的代碼就是一個爲一個整型數據分配內存的例子: int *pNumber;pNumber = new int;   第一行聲明一個指針pNumber。第二行爲一個整型數據分配一個內存空間,並讓pNumber指向這個新內存空間。下面是一個新例,這一次是用double雙精型: double *pDouble;pDouble = new double;   這種格式是一個規則,這樣寫你是不會錯的。  但動態分配又和前面的例子有什麼不一樣呢?就是在函數返回或執行完畢時,你分配的這塊內存區域是不會被刪除的因此咱們如今能夠用動態分配重寫上面的程序: #include <iostream.h>int *pPointer;void SomeFunction(){// 讓指針指向一個新的整型pPointer = new int;*pPointer = 25;}void main(){SomeFunction(); // 爲pPointer賦值cout<<"Value of *pPointer: "<<*pPointer<<endl; }   通讀這個程序,編譯並運行它,務必理解它是怎樣工做的。當SomeFunction 調用時,它分配了一個內存,並讓pPointer指向它。這一次,當函數返回時,新的內存區域被保留下來,因此pPointer始終指着有用的信息,這是由於了動態分配。可是你再仔細讀讀上面這個程序,雖然它獲得了正確結果,可仍有一個嚴重的錯誤。分配了內存,別忘了回收  太複雜了,怎麼會還有嚴重的錯誤!其實要改正並不難。問題是:你動態地分配了一個內存空間,可它毫不會被自動刪除。也就是說,這塊內存空間會一直存在,直到你告訴電腦你已經使用完了。可結果是,你並無告訴電腦你已再也不須要這塊內存空間了,因此它會繼續佔據着內存空間形成浪費,甚至你的程序運行完畢,其它程序運行時它還存在。當這樣的問題積累到必定程度,最終將致使系統崩潰。因此這是很重要的,在你用完它之後,請釋放它的空間,如: delete pPointer;   這樣就差很少了,你不得不當心。在這你終止了一個有效的指針(一個確實指向某個內存的指針)。  下面的程序,它不會浪費任何的內存:#include <iostream.h>int *pPointer;void SomeFunction(){// 讓指針指向一個新的整型pPointer = new int;*pPointer = 25;}void main(){SomeFunction(); //爲pPointer賦值cout<<"Value of *pPointer: "<<*pPointer<<endl;delete pPointer;}   只有一行與前一個程序不一樣,但就是這最後一行十分地重要。若是你不刪除它,你就會製造一塊兒「內存漏洞」,而讓內存逐漸地泄漏。  (譯者:假如在程序中調用了兩次SomeFunction,你又該如何修改這個程序呢?請讀者本身思考)傳遞指針到函數  傳遞指針到函數是很是有用的,也很容易掌握。若是咱們寫一個程序,讓一個數加上5,看一看這個程序完整嗎?: #include <iostream.h>void AddFive(int Number){Number = Number + 5;}void main(){int nMyNumber = 18;cout<<"My original number is "<<nMyNumber<<endl; AddFive(nMyNumber);cout<<"My new number is "<<nMyNumber<<endl; //獲得告終果23嗎?問題出在哪兒?}   問題出在函數AddFive裏用到的Number是變量nMyNumber的一個副本而傳遞給函數,而不是變量自己。所以, " Number = Number + 5" 這一行是把變量的副本加了5,而原始的變量在主函數main()裏依然沒變。試着運行這個程序,本身去體會一下。  要解決這個問題,咱們就要傳遞一個指針到函數,因此咱們要修改一下函數讓它能接受指針:把'void AddFive(int Number)' 改爲 'void AddFive(int* Number)' 。下面就是改過的程序,注意函數調用時要用&號,以表示傳遞的是指針: #include <iostream.h>void AddFive(int* Number){*Number = *Number + 5;}void main(){int nMyNumber = 18;cout<<"My original number is "<<nMyNumber<<endl; AddFive(&nMyNumber);cout<<"My new number is "<<nMyNumber<<endl; }  試着本身去運行它,注意在函數AddFive的參數Number前加*號的重要性:它告訴編譯器,咱們是把指針所指的變量加5。而不併指針本身加5。  最後,若是想讓函數返回指針的話,你能夠這麼寫: int * MyFunction();   在這句裏,MyFunction返回一個指向整型的指針。指向類的指針  指針在類中的操做要格外當心,你能夠用以下的辦法定義一個類: class MyClass{  public:  int m_Number;  char m_Character;};   接着你就能夠定義一個MyClass 類的變量了: MyClass thing;   你應該已經知道怎樣去定義一個指針了吧: MyClass *thing;   接着你能夠分配個內存空間給它: thing = new MyClass;   注意,問題出現了。你打算怎樣使用這個指針呢,一般你可能會寫'thing.m_Number',可是thing是類嗎,不,它是一個指向類的指針,它自己並不包含一個叫m_Number的變量。因此咱們必須用另外一種方法:就是把'.'(點號)換成 -> ,來看下面的例子: class MyClass{public:int m_Number;char m_Character;};void main(){MyClass *pPointer;pPointer = new MyClass;pPointer->m_Number = 10;pPointer->m_Character = 's';delete pPointer;}指向數組的指針  你也可讓指針指向一個數組,按下面的方法操做: int *pArray;pArray = new int[6];   程序會建立一個指針pArray,讓它指向一個有六個元素的數組。另一種方法,不用動態分配: int *pArray;int MyArray[6];pArray = &MyArray[0];   注意,&MyArray[0] 也能夠簡寫成 MyArray ,都表示是數組的第一個元素地址。但若是寫成pArray = &MyArray可能就會出問題,結果是 pArray 指向的是指向數組的指針(在一維數組中儘管與&MyArray[0]相等),而不是你想要的,在多維數組中很容易出錯。在數組中使用指針  一旦你定義了一個指向數組的指針,你該怎樣使用它呢?讓咱們來看一個例子,一個指向整型數組的指針:#include <iostream.h>void main(){int Array[3];Array[0] = 10;Array[1] = 20;Array[2] = 30;int *pArray;pArray = &Array[0];cout<<"pArray points to the value %d\n"<<*pArray<<endl;}  若是讓指針指向數組元素中的下一個,能夠用pArray++.也能夠用你應該能想到的pArray + 1,都會讓指針指向數組的下一個元素。要注意的是你在移動指針時,程序並不檢查你是否已經移動地超出了你定義的數組,也就是說你極可能經過上面的簡單指針加操做而訪問到數組之外的數據,而結果就是,可能會使系統崩潰,因此請格外當心。  固然有了pArray + 1,也能夠有pArray - 1,這種操做在循環中很經常使用,特別是while循環中。  另外一個須要注意的是,若是你定義了一個指向整型數的指針:int* pNumberSet ,你能夠把它看成是數組,如:pNumberSet[0] 和 *pNumberSet是相等的,pNumberSet[1]與*(pNumberSet + 1)也是相等的。  在這一節的最後提一個警告:若是你用 new 動態地分配了一個數組, int *pArray;pArray = new int[6];   別忘了回收, delete[] pArray;   這一句是告訴編譯器是刪除整個數組而不一個單獨的元素。千萬記住了。後話  還有一點要當心,別刪除一個根本就沒分配內存的指針,典型的是若是沒用new分配,就別用delete:void main(){  int number;  int *pNumber = number;  delete pNumber; // 錯誤 - *pNumber 沒有用new動態分配內存.}常見問題解答Q:爲何我在編譯程序時總是在 new 和 delete語句中出現'symbol undefined' 錯誤?A:new 和 delete都是C++在C上的擴展,這個錯誤是說編譯器認爲你如今的程序是C而不C++,固然會出錯了。看看你的文件名是否是.cpp結尾。Q:new 和 malloc有什麼不一樣?A:new 是C++中的關健字,用來分配內存的一個標準函數。若是沒有必要,請不要在C++中使用malloc。由於malloc是C中的語法,它不是爲面向對象的C++而設計的。Q:我能夠同時使用free 和 delete嗎?A:你應該注意的是,它們各自所匹配的操做不一樣。free只用在用malloc分配的內存操做中,而delete只用在用new分配的內存操做中。引用(寫給某些有能力的讀者)  這一節的內容不是個人這篇文章的中心,只是供某些有能力的讀者參考。  有些讀者常常問我關於引用和指針的問題,這裏我簡要地討論一下。  在前面指針的學習中,咱們知道(&)是讀做「什麼的地址」,但在下面的程序中,它是讀做「什麼的引用」int& Number = myOtherNumber;Number = 25;   引用有點像是一個指向myOtherNumber的指針,不一樣的是它是自動刪除的。因此他比指針在某些場合更有用。與上面等價的代碼是: int* pNumber = &myOtherNumber;*pNumber = 25;   指針與引用另外一個不一樣是你不能修改你已經定義好的引用,也就是說你不能改變它在聲明時所指的內容。舉個例子: int myFirstNumber = 25;int mySecondNumber = 20;int &myReference = myFirstNumber;myReference = mySecondNumber;//這一步能使myReference 改變嗎?cout<<myFristNumber<<endl;//結果是20仍是25?  當在類中操做時,引用的值必須在構造函數中設定,例:CMyClass::CMyClass(int &variable) : m_MyReferenceInCMyClass(variable){  // constructor code here}總結  這篇文章開始可能會較難掌握,因此最好是多讀幾遍。有些讀者暫時還不能理解,在這兒我再作一個簡要的總結:  指針是一個指向內存區域的變量,定義時在變量名前加上星號(*)(如:int *number)。  你能夠獲得任何一個變量的地址,只在變量名前加上&(如:pNumber = &my_number)。  你能夠用'new' 關鍵字動態分配內存。指針的類型必須與它所指的變量類型同樣(如:int *number 就不能指向 MyClass)。  你能夠傳遞一個指針到函數。必須用'delete'刪除你動態分配的內存。  你能夠用&array[0]而讓指針指向一個數組。  你必須用delete[]而不是delete來刪除動態分配的數組。  文章到這兒就差很少結束了,但這些並不就是指針全部的東西,像指向指針的指針等我尚未介紹,由於這些東西對於一個初學指針的人來講還太複雜了,我不能讓讀者一開始就被太複雜的東西而嚇走了。好了,到這兒吧,試着運行我上面寫的小程序,也多本身寫寫程序,你確定會進步不小的!
相關文章
相關標籤/搜索