什么是指針?
其實(shí)指針就像是其它變量一樣,所不同的是一般的變量包含的是實(shí)際的真實(shí)的數(shù)據(jù),而指針是一個(gè)指示器,它告訴程序在內(nèi)存的哪塊區(qū)域可以找到數(shù)據(jù)。這是一個(gè)非常重要的概念,有很多程序和算法都是圍繞指針而設(shè)計(jì)的,如鏈表。
開始學(xué)習(xí)
如何定義一個(gè)指針呢?就像你定義一個(gè)其它變量一樣,只不過你要在指針名字前加上一個(gè)星號。我們來看一個(gè)例子:下面這個(gè)程序定義了兩個(gè)指針,它們都是指向整型數(shù)據(jù)。
int* pNumberOne;
int* pNumberTwo;
你注意到在兩個(gè)變量名前的“p”前綴了嗎?這是程序員通常在定義指針時(shí)的一個(gè)習(xí)慣,以提高便程序的閱讀性,表示這是個(gè)指針,F(xiàn)在讓我們來初始化這兩個(gè)指針:
pNumberOne = &some_number;
pNumberTwo = &some_other_number;
&號讀作“什么的地址”,它表示返回的是變量在內(nèi)存中的地址而不是變量本身的值。在這個(gè)例子中,pNumberOne 等于some_number的地址,所以現(xiàn)在pNumberOne指向some_number. 如果現(xiàn)在我們在程序中要用到some_number,我們就可以使用pNumberOne.
我們來學(xué)習(xí)一個(gè)例子:在這個(gè)例子中你將學(xué)到很多,如果你對指針的概念一點(diǎn)都不了解,我建議你多看幾遍這個(gè)例子,指針是個(gè)很復(fù)雜的東西,但你會很快掌握它的。
這個(gè)例子用以增強(qiáng)你對上面所介紹內(nèi)容的了解。它是用C編寫的(注:原英文版是用C寫的代碼,譯者重新用C++改寫寫了所有代碼,并在DEV C++ 和VC++中編譯通過!)
#include
void main()
{
// 聲明變量:
int nNumber;
int *pPointer;
// 現(xiàn)在給它們賦值:
nNumber = 15;
pPointer = &nNumber;
//打印出變量nNumber的值:
cout<<"nNumber is equal to :"<< nNumber< // 現(xiàn)在通過指針改變nNumber的值: *pPointer = 25; //證明nNumber已經(jīng)被上面的程序改變 //重新打印出nNumber的值: cout<<"nNumber is equal to :"< } 通讀一下這個(gè)程序,編譯并運(yùn)行它,務(wù)必明白它是怎樣工作的。如果你完成了,準(zhǔn)備好,開始下一小節(jié)。 [NextPage] 陷井! 試一下,你能找出下面這段程序的錯(cuò)誤嗎? #include int *pPointer; void SomeFunction(); { int nNumber; nNumber = 25; //讓指針指向nNumber: pPointer = &nNumber; } void main() { SomeFunction(); //為pPointer賦值 //為什么這里失敗了?為什么沒有得到25 cout<<"Value of *pPointer: "<<*pPointer< } 這段程序先調(diào)用了SomeFunction函數(shù),創(chuàng)建了個(gè)叫nNumber的變量,接著讓指針pPointer指向了它?墒菃栴}出在哪兒呢?當(dāng)函數(shù)結(jié)束后,nNumber被刪掉了,因?yàn)檫@一個(gè)局部變量。局部變量在定義它的函數(shù)執(zhí)行完后都會被系統(tǒng)自動(dòng)刪掉。也就是說當(dāng)SomeFunction 函數(shù)返回主函數(shù)main()時(shí),這個(gè)變量已經(jīng)被刪掉,但pPointer還指著變量曾經(jīng)用過的但現(xiàn)在已不屬于這個(gè)程序的區(qū)域。如果你還不明白,你可以再讀讀這個(gè)程序,注意它的局部變量和全局變量,這些概念都非常重要。 但這個(gè)問題怎么解決呢?答案是動(dòng)態(tài)分配技術(shù)。注意這在C和C++中是不同的。由于大多數(shù)程序員都是用C++,所以我用到的是C++中常用的稱謂。 動(dòng)態(tài)分配 動(dòng)態(tài)分配是指針的關(guān)鍵技術(shù)。它是用來在不必定義變量的情況下分配內(nèi)存和讓指針去指向它們。盡管這么說可能會讓你迷惑,其實(shí)它真的很簡單。下面的代碼就是一個(gè)為一個(gè)整型數(shù)據(jù)分配內(nèi)存的例子: int *pNumber; pNumber = new int; 第一行聲明一個(gè)指針pNumber.第二行為一個(gè)整型數(shù)據(jù)分配一個(gè)內(nèi)存空間,并讓pNumber指向這個(gè)新內(nèi)存空間。下面是一個(gè)新例,這一次是用double雙精型: double *pDouble; pDouble = new double; 這種格式是一個(gè)規(guī)則,這樣寫你是不會錯(cuò)的。 但動(dòng)態(tài)分配又和前面的例子有什么不同呢?就是在函數(shù)返回或執(zhí)行完畢時(shí),你分配的這塊內(nèi)存區(qū)域是不會被刪除的所以我們現(xiàn)在可以用動(dòng)態(tài)分配重寫上面的程序: #include int *pPointer; void SomeFunction() { // 讓指針指向一個(gè)新的整型 pPointer = new int; *pPointer = 25; } void main() { SomeFunction(); // 為pPointer賦值 cout<<"Value of *pPointer: "<<*pPointer< } 通讀這個(gè)程序,編譯并運(yùn)行它,務(wù)必理解它是怎樣工作的。當(dāng)SomeFunction 調(diào)用時(shí),它分配了一個(gè)內(nèi)存,并讓pPointer指向它。這一次,當(dāng)函數(shù)返回時(shí),新的內(nèi)存區(qū)域被保留下來,所以pPointer始終指著有用的信息,這是因?yàn)榱藙?dòng)態(tài)分配。但是你再仔細(xì)讀讀上面這個(gè)程序,雖然它得到了正確結(jié)果,可仍有一個(gè)嚴(yán)重的錯(cuò)誤。 分配了內(nèi)存,別忘了回收太復(fù)雜了,怎么會還有嚴(yán)重的錯(cuò)誤!其實(shí)要改正并不難。問題是:你動(dòng)態(tài)地分配了一個(gè)內(nèi)存空間,可它絕不會被自動(dòng)刪除。也就是說,這塊內(nèi)存空間會一直存在,直到你告訴電腦你已經(jīng)使用完了?山Y(jié)果是,你并沒有告訴電腦你已不再需要這塊內(nèi)存空間了,所以它會繼續(xù)占據(jù)著內(nèi)存空間造成浪費(fèi),甚至你的程序運(yùn)行完畢,其它程序運(yùn)行時(shí)它還存在。當(dāng)這樣的問題積累到一定程度,最終將導(dǎo)致系統(tǒng)崩潰。所以這是很重要的,在你用完它以后,請釋放它的空間,如:delete pPointer;這樣就差不多了,你不得不小心。在這你終止了一個(gè)有效的指針(一個(gè)確實(shí)指向某個(gè)內(nèi)存的指針)。 [NextPage] 下面的程序,它不會浪費(fèi)任何的內(nèi)存: #include int *pPointer; void SomeFunction() { // 讓指針指向一個(gè)新的整型 pPointer = new int; *pPointer = 25; } void main() { SomeFunction(); //為pPointer賦值 cout<<"Value of *pPointer: "<<*pPointer< delete pPointer; } 只有一行與前一個(gè)程序不同,但就是這最后一行十分地重要。如果你不刪除它,你就會制造一起“內(nèi)存漏洞”,而讓內(nèi)存逐漸地泄漏。 (譯者:假如在程序中調(diào)用了兩次SomeFunction,你又該如何修改這個(gè)程序呢?請讀者自己思考) 傳遞指針到函數(shù)傳遞指針到函數(shù)是非常有用的,也很容易掌握。如果我們寫一個(gè)程序,讓一個(gè)數(shù)加上5,看一看這個(gè)程序完整嗎?: #include void AddFive(int Number) { Number = Number + 5; } void main() { int nMyNumber = 18; cout<<"My original number is "< AddFive(nMyNumber); cout<<"My new number is "< //得到了結(jié)果23嗎?問題出在哪兒? } 問題出在函數(shù)AddFive里用到的Number是變量nMyNumber的一個(gè)副本而傳遞給函數(shù),而不是變量本身。因此,“Number = Number + 5”這一行是把變量的副本加了5,而原始的變量在主函數(shù)main()里依然沒變。試著運(yùn)行這個(gè)程序,自己去體會一下。 [NextPage] 要解決這個(gè)問題,我們就要傳遞一個(gè)指針到函數(shù),所以我們要修改一下函數(shù)讓它能接受指針:把'void AddFive(int Number)' 改成 'void AddFive(int* Number)' 。下面就是改過的程序,注意函數(shù)調(diào)用時(shí)要用&號,以表示傳遞的是指針: #include void AddFive(int* Number) { *Number = *Number + 5; } void main() { int nMyNumber = 18; cout<<"My original number is "< AddFive(&nMyNumber); cout<<"My new number is "< } 試著自己去運(yùn)行它,注意在函數(shù)AddFive的參數(shù)Number前加*號的重要性:它告訴編譯器,我們是把指針?biāo)傅淖兞考?.而不并指針自己加5.最后,如果想讓函數(shù)返回指針的話,你可以這么寫:int * MyFunction();在這句里,MyFunction返回一個(gè)指向整型的指針。 指向類的指針指針在類中的操作要格外小心,你可以用如下的辦法定義一個(gè)類: class MyClass { public: int m_Number; char m_Character; }; 接著你就可以定義一個(gè)MyClass 類的變量了:MyClass thing; 你應(yīng)該已經(jīng)知道怎樣去定義一個(gè)指針了吧:MyClass *thing; 接著你可以分配個(gè)內(nèi)存空間給它:thing = new MyClass; 注意,問題出現(xiàn)了。你打算怎樣使用這個(gè)指針呢,通常你可能會寫'thing.m_Number',但是thing是類嗎?不,它是一個(gè)指向類的指針,它本身并不包含一個(gè)叫m_Number的變量。所以我們必須用另一種方法:就是把'.'(點(diǎn)號)換成 -> ,來看下面的例子: 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; } [NextPage] 指向數(shù)組的指針 你也可以讓指針指向一個(gè)數(shù)組,按下面的方法操作: int *pArray; pArray = new int[6]; 程序會創(chuàng)建一個(gè)指針pArray,讓它指向一個(gè)有六個(gè)元素的數(shù)組。另外一種方法,不用動(dòng)態(tài)分配: int *pArray; int MyArray[6]; pArray = &MyArray[0]; 注意,&MyArray[0] 也可以簡寫成 MyArray ,都表示是數(shù)組的第一個(gè)元素地址。但如果寫成pArray = &MyArray可能就會出問題,結(jié)果是 pArray 指向的是指向數(shù)組的指針(在一維數(shù)組中盡管與&MyArray[0]相等),而不是你想要的,在多維數(shù)組中很容易出錯(cuò)。 在數(shù)組中使用指針 一旦你定義了一個(gè)指向數(shù)組的指針,你該怎樣使用它呢?讓我們來看一個(gè)例子,一個(gè)指向整型數(shù)組的指針: #include 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< } 如果讓指針指向數(shù)組元素中的下一個(gè),可以用pArray++.也可以用你應(yīng)該能想到的pArray + 1,都會讓指針指向數(shù)組的下一個(gè)元素。要注意的是你在移動(dòng)指針時(shí),程序并不檢查你是否已經(jīng)移動(dòng)地超出了你定義的數(shù)組,也就是說你很可能通過上面的簡單指針加操作而訪問到數(shù)組以外的數(shù)據(jù),而結(jié)果就是,可能會使系統(tǒng)崩潰,所以請格外小心。 當(dāng)然有了pArray + 1,也可以有pArray - 1,這種操作在循環(huán)中很常用,特別是while循環(huán)中。 另一個(gè)需要注意的是,如果你定義了一個(gè)指向整型數(shù)的指針:int* pNumberSet ,你可以把它當(dāng)作是數(shù)組,如:pNumberSet[0] 和 *pNumberSet是相等的,pNumberSet[1]與*(pNumberSet + 1)也是相等的。 在這一節(jié)的最后提一個(gè)警告:如果你用 new 動(dòng)態(tài)地分配了一個(gè)數(shù)組, int *pArray; pArray = new int[6]; 別忘了回收, delete[] pArray; 這一句是告訴編譯器是刪除整個(gè)數(shù)組而不一個(gè)單獨(dú)的元素。千萬記住了。 [NextPage] 后話 這一句是告訴編譯器是刪除整個(gè)數(shù)組而不一個(gè)單獨(dú)的元素。千萬記住了。還有一點(diǎn)要小心,別刪除一個(gè)根本就沒分配內(nèi)存的指針,典型的是如果沒用new分配,就別用delete: void main() { int number; int *pNumber = number; delete pNumber; // 錯(cuò)誤 - *pNumber 沒有用new動(dòng)態(tài)分配內(nèi)存. } 常見問題解答 Q:為什么我在編譯程序時(shí)老是在 new 和 delete語句中出現(xiàn)'symbol undefined' 錯(cuò)誤? A:new 和 delete都是C++在C上的擴(kuò)展,這個(gè)錯(cuò)誤是說編譯器認(rèn)為你現(xiàn)在的程序是C而不C++,當(dāng)然會出錯(cuò)了?纯茨愕奈募遣皇恰pp結(jié)尾。 Q:new 和 malloc有什么不同? A:new 是C++中的關(guān)健字,用來分配內(nèi)存的一個(gè)標(biāo)準(zhǔn)函數(shù)。如果沒有必要,請不要在C++中使用malloc.因?yàn)閙alloc是C中的語法,它不是為面向?qū)ο蟮腃++而設(shè)計(jì)的。 Q:我可以同時(shí)使用free 和 delete嗎? A:你應(yīng)該注意的是,它們各自所匹配的操作不同。free只用在用malloc分配的內(nèi)存操作中,而delete只用在用new分配的內(nèi)存操作中。 引用(寫給某些有能力的讀者) 這一節(jié)的內(nèi)容不是我的這篇文章的中心,只是供某些有能力的讀者參考。 有些讀者經(jīng)常問我關(guān)于引用和指針的問題,這里我簡要地討論一下。 在前面指針的學(xué)習(xí)中,我們知道(&)是讀作“什么的地址”,但在下面的程序中,它是讀作“什么的引用” int& Number = myOtherNumber; Number = 25; 引用有點(diǎn)像是一個(gè)指向myOtherNumber的指針,不同的是它是自動(dòng)刪除的。所以他比指針在某些場合更有用。與上面等價(jià)的代碼是: int* pNumber = &myOtherNumber; *pNumber = 25; 指針與引用另一個(gè)不同是你不能修改你已經(jīng)定義好的引用,也就是說你不能改變它在聲明時(shí)所指的內(nèi)容。舉個(gè)例子: int myFirstNumber = 25; int mySecondNumber = 20; int &myReference = myFirstNumber; myReference = mySecondNumber;//這一步能使myReference 改變嗎? cout< 當(dāng)在類中操作時(shí),引用的值必須在構(gòu)造函數(shù)中設(shè)定,例: CMyClass::CMyClass(int &variable) : m_MyReferenceInCMyClass(variable) { // constructor code here } 總結(jié) 這篇文章開始可能會較難掌握,所以最好是多讀幾遍。有些讀者暫時(shí)還不能理解,在這兒我再做一個(gè)簡要的總結(jié):指針是一個(gè)指向內(nèi)存區(qū)域的變量,定義時(shí)在變量名前加上星號(*)(如:int *number)。 你可以得到任何一個(gè)變量的地址,只在變量名前加上&(如:pNumber = &my_number)。 你可以用'new' 關(guān)鍵字動(dòng)態(tài)分配內(nèi)存。指針的類型必須與它所指的變量類型一樣(如:int *number 就不能指向 MyClass)。 你可以傳遞一個(gè)指針到函數(shù)。必須用'delete'刪除你動(dòng)態(tài)分配的內(nèi)存。 你可以用&array[0]而讓指針指向一個(gè)數(shù)組。 你必須用delete[]而不是delete來刪除動(dòng)態(tài)分配的數(shù)組。 文章到這兒就差不多結(jié)束了,但這些并不就是指針?biāo)械臇|西,像指向指針的指針等我還沒有介紹,因?yàn)檫@些東西對于一個(gè)初學(xué)指針的人來說還太復(fù)雜了,我不能讓讀者一開始就被太復(fù)雜的東西而嚇走了。好了,到這兒吧,試著運(yùn)行我上面寫的小程序,也多自己寫寫程序,你肯定會進(jìn)步不小的!
計(jì)算機(jī)等級考試二級C++模擬試題15套(上機(jī))
2010年全國計(jì)算機(jī)等級考試考試報(bào)考指南
北京 | 天津 | 上海 | 江蘇 | 山東 |
安徽 | 浙江 | 江西 | 福建 | 深圳 |
廣東 | 河北 | 湖南 | 廣西 | 河南 |
海南 | 湖北 | 四川 | 重慶 | 云南 |
貴州 | 西藏 | 新疆 | 陜西 | 山西 |
寧夏 | 甘肅 | 青海 | 遼寧 | 吉林 |
黑龍江 | 內(nèi)蒙古 |