指標GUIDE
譯者序: 這是一篇我所見過的關於指標的最好的入門級文章,它可使初學者在很短的時間內掌握複雜的指標操作。雖然,現在的Java、C#等語言已經取消了指標,但作為一個C++程式設計師,指標的直接操作 什麼是指標?
|
int* pNumberOne; int* pNumberTwo; |
你注意到在兩個變數名前的“p”字首了嗎?這是程式設計師通常在定義指標時的一個習慣,以提高便程式的閱讀性,表示這是個指標。現在讓我們來初始化這兩個指標: |
pNumberOne = &some_number; pNumberTwo = &some_other_number; |
&號讀作“什麼的地址”,它表示返回的是變數在記憶體中的地址而不是變數本身的值。在這個例子中,pNumberOne 等於some_number的地址,所以現在pNumberOne指向some_number。 如果現在我們在程式中要用到some_number,我們就可以使用pNumberOne。
|
#include void main() // 現在給它們賦值: //列印出變數nNumber的值: // 現在透過指標改變nNumber的值: |
通讀一下這個程式,編譯並執行它,務必明白它是怎樣工作的。如果你完成了,準備好,開始下一小節。 陷井! |
#include int *pPointer; void SomeFunction(); //讓指標指向nNumber: void main() //為什麼這裡失敗了?為什麼沒有得到25 |
這段程式先呼叫了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 int *pPointer; void SomeFunction() void main() |
通讀這個程式,編譯並執行它,務必理解它是怎樣工作的。當SomeFunction 呼叫時,它分配了一個記憶體,並讓pPointer指向它。這一次,當函式返回時,新的記憶體區域被保留下來,所以pPointer始終指著有用的資訊,這是因為了動態分配。但是你再仔細讀讀上面這個程式,雖然它得到了正確結果,可仍有一個嚴重的錯誤。 |
分配了記憶體,別忘了回收 太複雜了,怎麼會還有嚴重的錯誤!其實要改正並不難。問題是:你動態地分配了一個記憶體空間,可它絕不會被自動刪除。也就是說,這塊記憶體空間會一直存在,直到你告訴電腦你已經使用完了。可結果是,你並沒有告訴電腦你已不再需要這塊記憶體空間了,所以它會繼續佔據著記憶體空間造成浪費,甚至你的程式執行完畢,其它程式執行時它還存在。當這樣的問題積累到一定程度,最終將導致系統崩潰。所以這是很重要的,在你用完它以後,請釋放它的空間,如: |
delete pPointer; |
這樣就差不多了,你不得不小心。在這你終止了一個有效的指標(一個確實指向某個記憶體的指標)。 下面的程式,它不會浪費任何的記憶體: |
#include int *pPointer; void SomeFunction() void main() delete pPointer; |
只有一行與前一個程式不同,但就是這最後一行十分地重要。如果你不刪除它,你就會製造一起“記憶體漏洞”,而讓記憶體逐漸地洩漏。 (譯者:假如在程式中呼叫了兩次SomeFunction,你又該如何修改這個程式呢?請讀者自己思考) 傳遞指標到函式 傳遞指標到函式是非常有用的,也很容易掌握。如果我們寫一個程式,讓一個數加上5,看一看這個程式完整嗎?: |
#include void AddFive(int Number) void main() cout<My new number is "< } |
問題出在函式AddFive裡用到的Number是變數nMyNumber的一個副本而傳遞給函式,而不是變數本身。因此, " Number = Number + 5" 這一行是把變數的副本加了5,而原始的變數在主函式main()裡依然沒變。試著執行這個程式,自己去體會一下。 要解決這個問題,我們就要傳遞一個指標到函式,所以我們要修改一下函式讓它能接受指標:把'void AddFive(int Number)' 改成 'void AddFive(int* Number)' 。下面就是改過的程式,注意函式呼叫時要用&號,以表示傳遞的是指標: |
#include void main() |
試著自己去執行它,注意在函式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 void main() pPointer->m_Number = 10; 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 void main() int *pArray; cout<pArray points to the value %dn"<} |
如果讓指標指向陣列元素中的下一個,可以用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; |
這一句是告訴編譯器是刪除整個陣列而不一個單獨的元素。千萬記住了。 後話 |
void main() { int number; int *pNumber = number; delete pNumber; // 錯誤 - *pNumber 沒有用new動態分配記憶體. } |
Q:new 和 malloc有什麼不同? Q:我可以同時使用free 和 delete嗎? 引用(寫給某些有能力的讀者) |
int& Number = myOtherNumber; Number = 25; |
引用有點像是一個指向myOtherNumber的指標,不同的是它是自動刪除的。所以他比指標在某些場合更有用。與上面等價的程式碼是: |
int* pNumber = &myOtherNumber; *pNumber = 25; |
指標與引用另一個不同是你不能修改你已經定義好的引用,也就是說你不能改變它在宣告時所指的內容。舉個例子: |
int myFirstNumber = 25; myReference = mySecondNumber;//這一步能使myReference 改變嗎? cout< |
當在類中操作時,引用的值必須在建構函式中設定,例: |
CMyClass::CMyClass(int &variable) : m_MyReferenceInCMyClass(variable) { // constructor code here } |
文章到這兒就差不多結束了,但這些並不就是指標所有的東西,像指向指標的指標等我還沒有介紹,因為這些東西對於一個初學指標的人來說還太複雜了,我不能讓讀者一開始就被太複雜的東西而嚇走了。好了,到這兒吧,試著執行我上面寫的小程式,也多自己寫寫程式,你肯定會進步不小的! |
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10957369/viewspace-1014619/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- NULL 指標、零指標、野指標Null指標
- 野指標 空指標指標
- 指標常量和常量指標指標
- C語言指標(三):陣列指標和字串指標C語言指標陣列字串
- 陣列指標,指標陣列陣列指標
- ARC中強指標與弱指標指標
- 控制指標與統計指標指標
- 陣列指標 指標陣列陣列指標
- 關於指標傳遞和指標的指標指標
- 指標指標
- 指向指標的指標指標
- 指標陣列與陣列指標指標陣列
- 詳解 常量指標和指標常量指標
- 陣列指標和指標陣列陣列指標
- 指標函式 和 函式指標指標函式
- 指標問題的一點體會(區別 [指向指標的指標] 與 [指標的指標] .) (轉)指標
- 第 10 節:複合型別-5. 指標 -- 指標與指標變數 -8. 多級指標型別指標變數
- C指標原理(15)-C指標基礎指標
- C指標原理(14)-C指標基礎指標
- GO 指標Go指標
- isa指標指標
- golang指標Golang指標
- C | 指標指標
- golang 指標Golang指標
- 指標 (轉)指標
- MACD指標Mac指標
- 雙指標指標
- HCBOLL指標指標
- 姜指標指標
- 智慧指標指標
- Go 陣列指標(指向陣列的指標)Go陣列指標
- C與指標 第六章 指標指標
- C語言指標(二) 指標變數 ----by xhxhC語言指標變數
- 指向常量資料的指標和常量指標指標
- 預算指標 技術指標 操作引數指標
- 誰說Java無指標, JAVA連結串列指標也好煩 - Java 指標迴歸Java指標
- C語言重點——指標篇(一文讓你完全搞懂指標)| 從記憶體理解指標 | 指標完全解析C語言指標記憶體
- 如何理解指向指標的指標?指標