Upload
emiko
View
25
Download
0
Embed Size (px)
DESCRIPTION
Chapter 08. Pointers and Pointer-Based Strings (Part II). 教學目標. 在這部分你會學到: 指標與陣列之間的關係 使用 const 指標 實作選擇排序法( selection sort ) sizeof 運算子. 8.9 指標與陣列的關係. 陣列的名稱可以被當成指標。 指標也可以用來作陣列索引( index )的運算。. 8.9 指標與陣列的關係. 陣列的名稱可以被當成指標。 用指標來存取陣列中的元素 例: b[n] 可以用 *(bPtr+n) 來表示 - PowerPoint PPT Presentation
Citation preview
Pointers and Pointer-Based Strings
(Part II)
在這部分你會學到: 指標與陣列之間的關係 使用 const 指標 實作選擇排序法( selection sort )sizeof 運算子
陣列的名稱可以被當成指標。 指標也可以用來作陣列索引( index )的運算。
陣列的名稱可以被當成指標。 用指標來存取陣列中的元素 例:
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;
陣列的名稱實際上代表了個陣列的開頭位址;假設我們有一個數字陣列,則可以用一個指向數字的指標來記住其中一個元素的記憶體位址。
如何取得開頭的位址?1. vPtr = v;2. vPtr = &v[0];
那麼,如何從一個位址中取得這個位址所儲存的 value ? 利用「 * 」:反參照運算子 如何得到 vPtr+1 、 vPtr+2 、…、 vPtr+4 這些位
址中所儲存的資料?*(vPtr+1)*(vPtr+2)*(vPtr+3)*(vPtr+4)
令vPtr = v;
• 問: vPtr 現在記錄的是哪一個元素的位址? &v[3] 等同於 vPtr+3 v[3] 等同於 *(vPtr+3) 陣列的名稱指標變數可以交互使用
vPtr[3] 等同於 v[3] *(vPtr+3) 等同於 *(v+3)
陣列的名稱指標變數可以互用。 例:
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 )
陣列 / 索引法( array/subscript notation )
指標 / 位移法( pointer/offset notation )
指標 / 位移法( pointer/offset notation )
陣列 / 位移法( array/offset notation )陣列 / 位移法( array/offset notation )
指標 / 索引法( array/subscript notation )
指標 / 索引法( array/subscript notation )
1 // Fig. 8.20: fig08_20.cpp
2 // Using subscripting and pointer notations with arrays.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 int main()
8 {
9 int b[] = { 10, 20, 30, 40 }; // create 4-element array b
10 int *bPtr = b; // set bPtr to point to array b
11
12 // output array b using array subscript notation
13 cout << "Array b printed with:\n\nArray subscript notation\n";
14
15 for ( int i = 0; i < 4; i++ )
16 cout << "b[" << i << "] = " << b[ i ] << '\n';
17
18 // output array b using the array name and pointer/offset notation
19 cout << "\nPointer/offset notation where "
20 << "the pointer is the array name\n";
21
22 for ( int offset1 = 0; offset1 < 4; offset1++ )
23 cout << "*(b + " << offset1 << ") = " << *( b + offset1 ) << '\n';
array / subscript notation
array /offset notation
24
25 // output array b using bPtr and array subscript notation
26 cout << "\nPointer subscript notation\n";
27
28 for ( int j = 0; j < 4; j++ )
29 cout << "bPtr[" << j << "] = " << bPtr[ j ] << '\n';
30
31 cout << "\nPointer/offset notation\n";
32
33 // output array b using bPtr and pointer/offset notation
34 for ( int offset2 = 0; offset2 < 4; offset2++ )
35 cout << "*(bPtr + " << offset2 << ") = "
36 << *( bPtr + offset2 ) << '\n';
37
38 return 0; // indicates successful termination
39 } // end main
Pointer/subscript notation
pointer/offset notation
Array b printed with: Array subscript notation b[0] = 10 b[1] = 20 b[2] = 30 b[3] = 40 Pointer/offset notation where the pointer is the array name *(b + 0) = 10 *(b + 1) = 20 *(b + 2) = 30 *(b + 3) = 40 Pointer subscript notation bPtr[0] = 10 bPtr[1] = 20 bPtr[2] = 30 bPtr[3] = 40 Pointer/offset notation *(bPtr + 0) = 10 *(bPtr + 1) = 20 *(bPtr + 2) = 30 *(bPtr + 3) = 40
雖然指標變數和陣列名稱可以互用,但要記得指標變數中的位址是可以修改的,陣列名稱所代表的位址則不能修改。 例如:
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 中的哪一個元素
加完的 bPtr 現在指向陣列 b 中的哪一個元素
請行會印出什麼?請行會印出什麼?
語法錯誤,為什麼?語法錯誤,為什麼?
雖然指標變數和陣列名稱可以互用,為簡潔起見,操作陣列時仍以陣列索引法來使用。 何時會用到指標來代替陣列?
宣告陣列的時候長度未定,先用指標代替宣告,日後再指定大小。儲存陣列的記憶體位址。
注意:不論是陣列或指標的表示法,都不會自動為使用者
提供範圍的判斷,因此在作索引存取或位移存取,都必須小心是否超出陣列的範圍。
1 // Fig. 8.21: fig08_21.cpp
2 // Copying a string using array notation and pointer notation.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 void copy1( char *, const char * ); // prototype
8 void copy2( char *, const char * ); // prototype
9
10 int main()
11 {
12 char string1[ 10 ];
13 char *string2 = "Hello";
14 char string3[ 10 ];
15 char string4[] = "Good Bye";
16
17 copy1( string1, string2 ); // copy string2 into string1
18 cout << "string1 = " << string1 << endl;
19
20 copy2( string3, string4 ); // copy string4 into string3
21 cout << "string3 = " << string3 << endl;
22 return 0; // indicates successful termination
23 } // end main
24
25 // copy s2 to s1 using array notation
26 void copy1( char * s1, const char * s2 )
27 {
28 // copying occurs in the for header
29 for ( int i = 0; ( s1[ i ] = s2[ i ] ) != '\0'; i++ )
30 ; // do nothing in body
31 } // end function copy1
32
33 // copy s2 to s1 using pointer notation
34 void copy2( char *s1, const char *s2 )
35 {
36 // copying occurs in the for header
37 for ( ; ( *s1 = *s2 ) != '\0'; s1++, s2++ )
38 ; // do nothing in body
39 } // end function copy2 string1 = Hello string3 = Good Bye
注意:參數宣告用指標,代表接收到記憶體位址。使用卻用索引的方式來便利取用元素
參數宣告用指標,代表接收到記憶體位址。使用反參照的方式來取用元素
將 s1 和 s2 累加,來將位址移動到下一個元素。
問題一:前面我們說過,陣列的名稱中所代表記憶體位址不能改變,何以這個地方,我們可以把 s1 和 s2 做 ++ ?
問題二:根據我們目前所學,加入我們有一個數字的陣列,討論為什麼當傳入整個陣列的時候,是傳參考呼叫,傳入某一個陣列元素的時候,卻變成傳值呼叫?
問題三: const 代表唯讀,那麼在參數的地方宣告「 s2 為 const 」,為什麼 s2 還可以 ++ ?
最小權限原則( Principle of least privilege ) 讓函式透過他的參數取得足夠的資料以便完成工作,
但不得擁有過大的存取權限。 在參數中使用 const :
規範參數為唯讀。 可以從此參數中讀取資料,但不得修改或變更其參數。
將一個指標變數宣告為 const ?const 指標
例如: const char *s2 請問是 s2 中的內容(記憶體位址)不能改,還是 s2 所
指向的位址中的內容不能改?s2 k
由於指標是間接取值,在使用 const 時,有四種方式來分別指定不同的存取權限: 指向非常數( non-constant )資料的非常數指標
指標指向的位址可以變更,指向位址的內容也可以改 指向常數資料的非常數指標
指標指向的位址可以變更,指向位址的內容不能改 指向非常數資料的常數指標
指標指向的位址不能變更,但指向位址的內容可以改 指向常數資料的常數指標
指標指向的位址不能變更,指向位址的內容也不能改
1 // Fig. 8.10: fig08_10.cpp
2 // Converting lowercase letters to uppercase letters
3 // using a non-constant pointer to non-constant data.
4 #include <iostream>
5 using std::cout;
6 using std::endl;
7
8 #include <cctype> // prototypes for islower and toupper
9 using std::islower;
10 using std::toupper;
11
12 void convertToUppercase( char * );
13
14 int main()
15 {
16 char phrase[] = "characters and $32.98";
17
18 cout << "The phrase before conversion is: " << phrase;
19 convertToUppercase( phrase );
20 cout << "\nThe phrase after conversion is: " << phrase << endl;
21 return 0; // indicates successful termination
22 } // end main
convertToUppercase 函式具有修改參數的權限
沒有加 const
23
24 // convert string to uppercase letters
25 void convertToUppercase( char *sPtr )
26 {
27 while ( *sPtr != '\0' ) // loop while current character is not '\0'
28 {
29 if ( islower( *sPtr ) ) // if character is lowercase,
30 *sPtr = toupper( *sPtr ); // convert to uppercase
31
32 sPtr++; // move sPtr to next character in string
33 } // end while
34 } // end function convertToUppercase The phrase before conversion is: characters and $32.98 The phrase after conversion is: CHARACTERS AND $32.98
參數 sPtr 是一個指向非常數資料的非常數指標
如果參數為小寫字元則函式islower 回傳 true
如果是小寫則回傳大寫字元,如果不是,則回傳原來的字元(請注意, sPtr所指向的位址是可以被修改的)
修改 sPtr 中所儲存的記憶體位址
指標內的位址可以改,但指向的位址中的資料不能改 提供 pass-by-reference (可以藉由位址找到原引數的
內容),但藉此保護其資料。 宣告方式:
將 const 寫在型態的前面。 例如:
const char *s2;
s2 k
這樣寫的意思,代表 s2 的內容可以改,但s2 所指向的位址 k 中的內容不能改。
1 // Fig. 8.12: fig08_12.cpp
2 // Attempting to modify data through a
3 // non-constant pointer to constant data.
4
5 void f( const int * ); // prototype
6
7 int main()
8 {
9 int y;
10
11 f( &y ); // f attempts illegal modification
12 return 0; // indicates successful termination
13 } // end main
將變數 y 的記憶體位址傳給函式f
參數是一個指向常數資料的非常數指標
14
15 // xPtr cannot modify the value of constant variable to which it points
16 void f( const int *xPtr )
17 {
18 *xPtr = 100; // error: cannot modify a const object
19 } // end function f
Borland C++ command-line compiler error message:
Error E2024 fig08_12.cpp 18: Cannot modify a const object in function f(const int *)
Microsoft Visual C++ compiler error message:
c:\cpphtp5_examples\ch08\Fig08_12\fig08_12.cpp(18) : error C2166: l-value specifies const object
GNU C++ compiler error message:
fig08_12.cpp: In function `void f(const int*)': fig08_12.cpp:18: error: assignment of read-only location
嘗試透過反參照運算子修改xPtr指向位址的內容
編譯錯誤
1 // Fig. 8.11: fig08_11.cpp
2 // Printing a string one character at a time using
3 // a non-constant pointer to constant data.
4 #include <iostream>
5 using std::cout;
6 using std::endl;
7
8 void printCharacters( const char * ); // print using pointer to const data
9
10 int main()
11 {
12 const char phrase[] = "print characters of a string";
13
14 cout << "The string is:\n";
15 printCharacters( phrase ); // print characters in phrase
16 cout << endl;
17 return 0; // indicates successful termination
18 } // end main
19
20 // sPtr can be modified, but it cannot modify the character to which
21 // it points, i.e., sPtr is a "read-only" pointer
22 void printCharacters( const char *sPtr )
23 {
24 for ( ; *sPtr != '\0'; sPtr++ ) // no initialization
25 cout << *sPtr; // display character without modification
26 } // end function printCharacters The string is: print characters of a string
參數是一個指向常數資料的非常數指標
傳指標給函式
sPtr 是一個指向常數資料的非常數指標, sPtr 裡面所儲存的位址可以改(非常數),指向位址的資料不能改(常數)。
因為 sPtr 是非常數指標,所以可以作 ++ 的動作。
當大型的資料傳進來當參數,且被呼叫的函式不需更改它們,可使用指向常數資料的指標或參照,以達到傳址呼叫的高效率。 因為是指向常數資料,因此可以為原參數的內容提供保
護。
指標內的位址不可以改,但指向位址中的資料可以改永遠指向同一個記憶體位址,或透過索引法或位移法來存
取 陣列名稱在預設情況下,為一個指向常數資料的常數指標
數值可以透過反參照運算子來修改 在宣告的時候必須要給初始值(初始的位址)。 怎麼宣告?將 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本身所儲存的位址不能改
1 // Fig. 8.13: fig08_13.cpp
2 // Attempting to modify a constant pointer to non-constant data.
3
4 int main()
5 {
6 int x, y;
7
8 // ptr is a constant pointer to an integer that can
9 // be modified through ptr, but ptr always points to the
10 // same memory location.
11 int * const ptr = &x; // const pointer must be initialized
12
13 *ptr = 7; // allowed: *ptr is not const
14 ptr = &y; // error: ptr is const; cannot assign to it a new address
15 return 0; // indicates successful termination
16 } // end main
Borland C++ command-line compiler error message:
Error E2024 fig08_13.cpp 14: Cannot modify a const object in function main()s
Microsoft Visual C++ compiler error message:
c:\cpphtp5e_examples\ch08\Fig08_13\fig08_13.cpp(14) : error C2166: l-value specifies const object GNU C++ compiler error message: fig08_13.cpp: In function `int main()': fig08_13.cpp:14: error: assignment of read-only variable `ptr'
ptr 是一個指向非常數資料的常數指標,注意一開始的時候就要給初始值
可以透過指標來修改 x的內容
但不可以修改 ptr 當中以存放的位址
上述寫法會發生編譯錯誤
指標內的位址不能改,但指向位址中的資料也不能改 存取權最小 宣告方式 在最前面要加 const 在「 * 」後面也要加 const
1 // Fig. 8.14: fig08_14.cpp
2 // Attempting to modify a constant pointer to constant data.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 int main()
8 {
9 int x = 5, y;
10
11 // ptr is a constant pointer to a constant integer.
12 // ptr always points to the same location; the integer
13 // at that location cannot be modified.
14 const int *const ptr = &x;
15
16 cout << *ptr << endl;
17
18 *ptr = 7; // error: *ptr is const; cannot assign new value
19 ptr = &y; // error: ptr is const; cannot assign new address
20 return 0; // indicates successful termination
21 } // end main
ptr 是一個指向常數資料的常數指標(注意要給初始值)
因為 *ptr 是常數,所以不能修改
因為 ptr 也是常數,所以也不能修改
以上的行為都會造成編譯的錯誤
何謂選擇排序法( 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
令 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 中找到最小值?如何從元素 i到 n-1 中找到最小值?
請以 selection sort 來說明以下陣列排序的過程:
34 56
0 1
4 10
2 3
77 51
4 5
93 30
6 7
5 52
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
0 1 2 3 4 5 6 7 8 9 10
1 // Fig. 8.15: fig08_15.cpp
2 // This program puts values into an array, sorts the values into
3 // ascending order and prints the resulting array.
4 #include <iostream>
5 using std::cout;
6 using std::endl;
7
8 #include <iomanip>
9 using std::setw;
10
11 void selectionSort( int * const, const int ); // prototype
12 void swap( int * const, int * const ); // prototype
13
14 int main()
15 {
16 const int arraySize = 10;
17 int a[ arraySize ] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 };
18
19 cout << "Data items in original order\n";
20
21 for ( int i = 0; i < arraySize; i++ )
22 cout << setw( 4 ) << a[ i ];
23
24 selectionSort( a, arraySize ); // sort the array
25
26 cout << "\nData items in ascending order\n";
27
28 for ( int j = 0; j < arraySize; j++ )
29 cout << setw( 4 ) << a[ j ];
30
31 cout << endl;
32 return 0; // indicates successful termination
33 } // end main
34
35 // function to sort an array
36 void selectionSort( int * const array, const int size )
37 {
38 int smallest; // index of smallest element
39
40 // loop over size - 1 elements
41 for ( int i = 0; i < size - 1; i++ )
42 {
43 smallest = i; // first index of remaining array
44
45 // loop to find index of smallest element
46 for ( int index = i + 1; index < size; index++ )
47
48 if ( array[ index ] < array[ smallest ] )
49 smallest = index;
50
51 swap( &array[ i ], &array[ smallest ] );
52 } // end if
53 } // end function selectionSort
array 是一個指向 ______ 資料的_______ 指標
size 是用來傳入 array 的大小,應不會對他的內容作修改,因此宣告為 const ,代表只能讀,不能作修改
注意這個變數 smallest ,用來記住目前最小值是哪個元素(用他的索引來代表),一開始,初始化為 i(因為從 i開始找)
54
55 // swap values at memory locations to which
56 // element1Ptr and element2Ptr point
57 void swap( int * const element1Ptr, int * const element2Ptr )
58 {
59 int hold = *element1Ptr;
60 *element1Ptr = *element2Ptr;
61 *element2Ptr = hold;
62 } // end function swap
Data items in original order 2 6 4 8 10 12 89 68 45 37
Data items in ascending order 2 4 6 8 10 12 37 45 68 89
請問 const 在這裡的意義?
定義 回傳其運算元的大小(單位:位元組( bytes )) 如果運算元提供的是一個陣列的名稱,則會計算出
(一個元素的大小 ) * ( 元素的個數 )換言之,就是計算出陣列佔有多少個位元組
範例:假設一個 int 有 4 個 bytes int myArray[ 10 ]; cout << sizeof( myArray );
將會印出? sizeof 接受的運算元可以是
變數名稱 型態名稱 常數
雖然陣列名稱可以和指標變數互用,但使用sizeof 時,有以下的問題需注意,運算元是一個陣列的時候才會計算出陣列的總位元組大小 .
運算元是指標,則回傳的是指標的位元組大小。
1 // Fig. 8.16: fig08_16.cpp
2 // Sizeof operator when used on an array name
3 // returns the number of bytes in the array.
4 #include <iostream>
5 using std::cout;
6 using std::endl;
7
8 size_t getSize( double * ); // prototype
9
10 int main()
11 {
12 double array[ 20 ]; // 20 doubles; occupies 160 bytes on our system
13
14 cout << "The number of bytes in the array is " << sizeof( array );
15
16 cout << "\nThe number of bytes returned by getSize is "
17 << getSize( array ) << endl;
18 return 0; // indicates successful termination
19 } // end main
20
21 // return size of ptr
22 size_t getSize( double *ptr )
23 {
24 return sizeof( ptr );
25 } // end function getSize The number of bytes in the array is 160 The number of bytes returned by getSize is 4
如果運算元是陣列,則回傳的是陣列的總 bytes 數
函式 getSize回傳的是用來儲存array這個位址所需要的 byte大小
Ptr 這個指標變數的大小
如何計算陣列的長度double realArray[ 22 ];
int length = sizeof realArray / sizeof( double );只有在運算元是型態的時候才需要加刮號,可是一般為了避免混淆,在此建議使用 sizeof 的時候,將所有的運算元加上刮號。
1 // Fig. 8.17: fig08_17.cpp
2 // Demonstrating the sizeof operator.
3 #include <iostream>
4 using std::cout;
5 using std::endl;
6
7 int main()
8 {
9 char c; // variable of type char
10 short s; // variable of type short
11 int i; // variable of type int
12 long l; // variable of type long
13 float f; // variable of type float
14 double d; // variable of type double
15 long double ld; // variable of type long double
16 int array[ 20 ]; // array of int
17 int *ptr = array; // variable of type int *
18
19 cout << "sizeof c = " << sizeof c
20 << "\tsizeof(char) = " << sizeof( char )
21 << "\nsizeof s = " << sizeof s
22 << "\tsizeof(short) = " << sizeof( short )
23 << "\nsizeof i = " << sizeof i
24 << "\tsizeof(int) = " << sizeof( int )
25 << "\nsizeof l = " << sizeof l
26 << "\tsizeof(long) = " << sizeof( long )
27 << "\nsizeof f = " << sizeof f
28 << "\tsizeof(float) = " << sizeof( float )
29 << "\nsizeof d = " << sizeof d
30 << "\tsizeof(double) = " << sizeof( double )
31 << "\nsizeof ld = " << sizeof ld
32 << "\tsizeof(long double) = " << sizeof( long double )
33 << "\nsizeof array = " << sizeof array
34 << "\nsizeof ptr = " << sizeof ptr << endl;
35 return 0; // indicates successful termination
36 } // end main
sizeof c = 1 sizeof(char) = 1 sizeof s = 2 sizeof(short) = 2 sizeof i = 4 sizeof(int) = 4 sizeof l = 4 sizeof(long) = 4 sizeof f = 4 sizeof(float) = 4 sizeof d = 8 sizeof(double) = 8 sizeof ld = 8 sizeof(long double) = 8 sizeof array = 80 sizeof ptr = 4