1 / 40

Chapter 08

Chapter 08. Pointers and Pointer-Based Strings (Part II). 教學目標. 在這部分你會學到: 指標與陣列之間的關係 使用 const 指標 實作選擇排序法( selection sort ) sizeof 運算子. 8.9 指標與陣列的關係. 陣列的名稱可以被當成指標。 指標也可以用來作陣列索引( index )的運算。. 8.9 指標與陣列的關係. 陣列的名稱可以被當成指標。 用指標來存取陣列中的元素 例: b[n] 可以用 *(bPtr+n) 來表示

emiko
Download Presentation

Chapter 08

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Chapter 08 Pointers and Pointer-Based Strings (Part II)

  2. 教學目標 • 在這部分你會學到: • 指標與陣列之間的關係 • 使用const指標 • 實作選擇排序法(selection sort) • sizeof運算子

  3. 8.9 指標與陣列的關係 • 陣列的名稱可以被當成指標。 • 指標也可以用來作陣列索引(index)的運算。

  4. 8.9 指標與陣列的關係 • 陣列的名稱可以被當成指標。 • 用指標來存取陣列中的元素 • 例: • b[n]可以用*(bPtr+n)來表示 • 指標/位移法(pointer/offset notation) int v[ 5 ]; int *vPtr; vPtr = v; for (int i=0; i<5; i++) v[i] = 0; for (int i=0; i<5; i++) *(vPtr + i) = 0;

  5. 陣列的名稱實際上代表了個陣列的開頭位址;假設我們有一個數字陣列,則可以用一個指向數字的指標來記住其中一個元素的記憶體位址。陣列的名稱實際上代表了個陣列的開頭位址;假設我們有一個數字陣列,則可以用一個指向數字的指標來記住其中一個元素的記憶體位址。 • 如何取得開頭的位址? • vPtr = v; • vPtr = &v[0];

  6. 指標可以透過位移的方式來改變其中的記憶體位址:指標可以透過位移的方式來改變其中的記憶體位址: • (vPtr+1)的結果並非等於3001;所謂的「+1」,代表移動一個int。 • 為什麼是int?因為vPtr是一個指向int的指標。 • 因此vPtr+1所得到記憶體位址應該為3004 • 通常一個int的大小為4個位元組(bytes) • 令 • vPtr=v; • 請根據上圖,說明vPtr+1、vPtr+2、…、vPtr+4分別代表陣列中哪個元素的記憶體位址?

  7. 那麼,如何從一個位址中取得這個位址所儲存的value?那麼,如何從一個位址中取得這個位址所儲存的value? • 利用「*」:反參照運算子 • 如何得到vPtr+1、vPtr+2、…、vPtr+4這些位址中所儲存的資料? • *(vPtr+1) • *(vPtr+2) • *(vPtr+3) • *(vPtr+4)

  8. • vPtr = v; • 問:vPtr現在記錄的是哪一個元素的位址? • &v[3]等同於vPtr+3 • v[3]等同於*(vPtr+3) • 陣列的名稱指標變數可以交互使用 • vPtr[3]等同於v[3] • *(vPtr+3)等同於*(v+3)

  9. 8.9 指標與陣列的關係 • 陣列的名稱指標變數可以互用。 • 例: int b[ 5 ]; int *bPtr; bPtr = b; for (int i=0; i<5; i++) b[i] = 0; for (int i=0; i<5; i++) *(bPtr + i) = 0; for (int i=0; i<5; i++) *(b + i) = 0; for (int i=0; i<5; i++) bPtr[i] = 0; 陣列/索引法(array/subscript notation) 指標/位移法(pointer/offset notation) 陣列/位移法(array/offset notation) 指標/索引法(array/subscript notation)

  10. array / subscript notation array /offset notation

  11. Pointer/subscript notation pointer/offset notation

  12. 常見的程式設計錯誤8.14 • 雖然指標變數和陣列名稱可以互用,但要記得指標變數中的位址是可以修改的,陣列名稱所代表的位址則不能修改。 • 例如: int b[ 5 ]= {3, 4, 2, 6, 1};int *bPtr;bPtr = b; bPtr++; //bPtr = bPtr + 1 cout << *bPtr << endl; cout << *b << endl; b++; cout << *b << endl; 加完的bPtr現在指向陣列b中的哪一個元素 請行會印出什麼? 語法錯誤,為什麼?

  13. 良好的程式設計習慣 8.14 • 雖然指標變數和陣列名稱可以互用,為簡潔起見,操作陣列時仍以陣列索引法來使用。 • 何時會用到指標來代替陣列? • 宣告陣列的時候 • 長度未定,先用指標代替宣告,日後再指定大小。 • 儲存陣列的記憶體位址。 • 注意: • 不論是陣列或指標的表示法,都不會自動為使用者提供範圍的判斷,因此在作索引存取或位移存取,都必須小心是否超出陣列的範圍。

  14. 注意:參數宣告用指標,代表接收到記憶體位址。使用卻用索引的方式來便利取用元素注意:參數宣告用指標,代表接收到記憶體位址。使用卻用索引的方式來便利取用元素 參數宣告用指標,代表接收到記憶體位址。使用反參照的方式來取用元素 將s1和s2累加,來將位址移動到下一個元素。 問題一:前面我們說過,陣列的名稱中所代表記憶體位址不能改變,何以這個地方,我們可以把s1和s2做++? 問題二:根據我們目前所學,加入我們有一個數字的陣列,討論為什麼當傳入整個陣列的時候,是傳參考呼叫,傳入某一個陣列元素的時候,卻變成傳值呼叫? 問題三:const代表唯讀,那麼在參數的地方宣告「s2為const」,為什麼s2還可以++?

  15. 8.5 使用const指標 • 最小權限原則( Principle of least privilege ) • 讓函式透過他的參數取得足夠的資料以便完成工作,但不得擁有過大的存取權限。 • 在參數中使用const: • 規範參數為唯讀。 • 可以從此參數中讀取資料,但不得修改或變更其參數。 • 將一個指標變數宣告為const? • const指標 例如:const char *s2 • 請問是s2中的內容(記憶體位址)不能改,還是s2所指向的位址中的內容不能改? s2 k

  16. 8.5 使用const指標 • 由於指標是間接取值,在使用const時,有四種方式來分別指定不同的存取權限: • 指向非常數(non-constant)資料的非常數指標 • 指標指向的位址可以變更,指向位址的內容也可以改 • 指向常數資料的非常數指標 • 指標指向的位址可以變更,指向位址的內容不能改 • 指向非常數資料的常數指標 • 指標指向的位址不能變更,但指向位址的內容可以改 • 指向常數資料的常數指標 • 指標指向的位址不能變更,指向位址的內容也不能改

  17. 1. 指向非常數資料的非常數指標 沒有加const convertToUppercase函式具有修改參數的權限

  18. 參數sPtr是一個指向非常數資料的非常數指標 如果參數為小寫字元則函式islower回傳true 如果是小寫則回傳大寫字元,如果不是,則回傳原來的字元(請注意,sPtr所指向的位址是可以被修改的) 修改sPtr中所儲存的記憶體位址

  19. 2. 指向常數資料的非常數指標 • 指標內的位址可以改,但指向的位址中的資料不能改 • 提供pass-by-reference(可以藉由位址找到原引數的內容),但藉此保護其資料。 • 宣告方式: • 將const寫在型態的前面。 • 例如: const char *s2; s2 k 這樣寫的意思,代表s2的內容可以改,但s2所指向的位址k中的內容不能改。

  20. 參數是一個指向常數資料的非常數指標 將變數y的記憶體位址傳給函式f 嘗試透過反參照運算子修改xPtr指向位址的內容 編譯錯誤

  21. 參數是一個指向常數資料的非常數指標 傳指標給函式 sPtr是一個指向常數資料的非常數指標,sPtr裡面所儲存的位址可以改(非常數),指向位址的資料不能改(常數)。 因為sPtr是非常數指標,所以可以作++的動作。

  22. 增進效能的小技巧 8.1 • 當大型的資料傳進來當參數,且被呼叫的函式不需更改它們,可使用指向常數資料的指標或參照,以達到傳址呼叫的高效率。 • 因為是指向常數資料,因此可以為原參數的內容提供保護。

  23. 3. 指向非常數資料的常數指標 • 指標內的位址不可以改,但指向位址中的資料可以改 • 永遠指向同一個記憶體位址,或透過索引法或位移法來存取 • 陣列名稱在預設情況下,為一個指向常數資料的常數指標 • 數值可以透過反參照運算子來修改 • 在宣告的時候必須要給初始值(初始的位址)。 • 怎麼宣告?將const放在指標變數名稱的前面(*的後面) int * const bPtr = &b[0]; int b[ 5 ]= {3, 4, 2, 6, 1}; b[1] = 5; *(b+2) = 3; b++; cout << *b << endl; b是一個指向非常數資料的常數指標,所以指向位址的內容可以改 但是b本身所儲存的位址不能改

  24. ptr是一個指向非常數資料的常數指標,注意一開始的時候就要給初始值ptr是一個指向非常數資料的常數指標,注意一開始的時候就要給初始值 可以透過指標來修改x的內容 但不可以修改ptr當中以存放的位址 上述寫法會發生編譯錯誤

  25. 4. 指向常數資料的常數指標 • 指標內的位址不能改,但指向位址中的資料也不能改 • 存取權最小 • 宣告方式 • 在最前面要加const • 在「*」後面也要加const

  26. ptr是一個指向常數資料的常數指標(注意要給初始值)ptr是一個指向常數資料的常數指標(注意要給初始值) 因為*ptr是常數,所以不能修改 因為ptr也是常數,所以也不能修改 以上的行為都會造成編譯的錯誤

  27. 8.6 使用傳址呼叫的選擇排序法 • 何謂選擇排序法(Selection sort) • 試問:如何將一個有n個數字的陣列排序? • 從元素0~n-1中找最小的,將他放在陣列的開頭(0) • 原來存在陣列0的元素怎麼辦? • 再從1~n-1中找最大的,將他放在陣列1的位置 • 再從2~n-1中找最大的,將他放在陣列2的位置 • 以下類推… • 從i~n-1中找最大的,把他放在陣列i的位置 • 最後從n-2~n-1中找最大的,將他放在陣列n-2的位置 • 請問:i的值從多少跑到多少? 0 1 n-1

  28. 演算法 • 令array是一個數字陣列,一個n代表陣列中的數字個數 for (int i = 0; i < n – 1; i++) { Find the smallest number from i to n-1; array[i] = smallest; } 如何從元素i到n-1中找到最小值?

  29. 範例 • 請以selection sort來說明以下陣列排序的過程: 0 1 2 3 4 5 6 7 8 9 10 34 56 4 10 77 51 93 30 5 52 0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10

  30. array是一個指向______資料的_______指標 size是用來傳入array的大小,應不會對他的內容作修改,因此宣告為const,代表只能讀,不能作修改 注意這個變數smallest,用來記住目前最小值是哪個元素(用他的索引來代表),一開始,初始化為i(因為從i開始找)

  31. 請問const在這裡的意義?

  32. 8.7 sizeof運算子 • 定義 • 回傳其運算元的大小(單位:位元組(bytes)) • 如果運算元提供的是一個陣列的名稱,則會計算出 (一個元素的大小) * (元素的個數 ) • 換言之,就是計算出陣列佔有多少個位元組 • 範例:假設一個int有4個bytesint myArray[ 10 ]; cout << sizeof( myArray ); 將會印出? • sizeof接受的運算元可以是 • 變數名稱 • 型態名稱 • 常數

  33. 常見的程式設計錯誤 8.7 • 雖然陣列名稱可以和指標變數互用,但使用sizeof時,有以下的問題需注意,運算元是一個陣列的時候才會計算出陣列的總位元組大小. • 運算元是指標,則回傳的是指標的位元組大小。

  34. 如果運算元是陣列,則回傳的是陣列的總bytes數如果運算元是陣列,則回傳的是陣列的總bytes數 函式getSize回傳的是用來儲存array這個位址所需要的byte大小 Ptr這個指標變數的大小

  35. 8.7 sizeof運算子 • 如何計算陣列的長度 double realArray[ 22 ]; int length = sizeof realArray / sizeof( double ); • 只有在運算元是型態的時候才需要加刮號,可是一般為了避免混淆,在此建議使用sizeof的時候,將所有的運算元加上刮號。

More Related