Download presentation
Presentation is loading. Please wait.
1
第7章 陣列與指標 7-1 陣列的基礎 7-2 一維陣列的處理 7-3 二維與多維陣列的處理 7-4 陣列的函數參數
7-5 指標與記憶體位址 7-6 指標與陣列 7-7 指向函數的指標
2
7-1 陣列的基礎 7-1-1 什麼是陣列 7-1-2 靜態記憶體配置 7-1-3 為什麼使用陣列
3
7-1-1 什麼是陣列 「陣列」(Arrays)是一種程式語言的基本資料結構,可以將C語言資料型態的變數集合起來,使用一個名稱代表,然後以索引值存取元素,每一個元素相當於一個變數,如下圖所示:
4
7-1-2 靜態記憶體配置 C語言的靜態記憶體配置是指在編譯階段,程式碼就已經配置宣告變數所需的記憶體空間。
例如:在本章前宣告基本資料型態的變數或整數陣列data,如下所示: int a, b, c; int data[100]; 上述變數和陣列,在編譯階段就已經配置所需的記憶體空間,每一個整數佔用4個位元組,陣列宣告100個元素,共需100x4共400位元組的記憶空間。
5
7-1-3 為什麼使用陣列 第一種方法使用數個變數儲存成績,這種方法的擴充性很差,如果小考次數改變,增加為10、50、100次或減少為3次,程式都需要大幅修改計算總分部分的程式碼。 第二種方法使用陣列儲存成績擁有較好的擴充性,當小考次數更改時,只需更改陣列大小,就可以使用相同的for迴圈來計算成績,更改迴圈次數還可以適用在100、200次的成績計算,而不用寫出冗長的加法運算式。
6
7-2 一維陣列的處理 7-2-1 宣告一維陣列 7-2-2 使用迴圈存取陣列元素
7
7-2 一維陣列的處理 「一維陣列」(One-dimensional Arrays)屬於最基本的陣列結構,只擁有一個索引,如同現實生活中的單排信箱,可以使用信箱號碼取出指定門牌的信件。
8
7-2-1 宣告一維陣列-語法 C語言的陣列宣告分成三個部分:陣列型態、陣列名稱和陣列維度,其宣告語法,如下所示:
陣列型態 陣列名稱[整數常數] 上述語法宣告一維陣列,因為只有一個「[]」(一個「[]」表示一維,二維就有2個),因為陣列是同一種資料型態的變數集合,如同基本資料型態的宣告,陣列型態就是陣列元素的資料型態,整數常數是陣列大小,也就是陣列擁有多少個元素。
9
7-2-1 宣告一維陣列-宣告 現在我們就可以宣告一維陣列,例如:一個浮點數陣列scores,如下所示:
double scores[5]; 上述程式碼宣告double資料型態的陣列,陣列名稱為scores,整數常數5表示陣列擁有5個元素。
10
7-2-1 宣告一維陣列-圖例 程式執行時會配置記憶體給宣告的陣列, 如下圖所示:
11
7-2-1 宣告一維陣列-存取陣列元素1 接著就可以從索引值0開始(C語言的陣列索引是從0開始),指定陣列元素的值,如下所示:
scores[0] = 45.6; scores[1] = 78.9; scores[2] = 97.3; scores[3] = 56.7; scores[4] = 45.8; 上述程式碼指定陣列元素的值,此時5個陣列元素,如下圖所示:
12
7-2-1 宣告一維陣列-存取陣列元素2 在運算式取得陣列元素的值來進行計算,如下所示: 上述程式碼就是各陣列元素相加的運算式。
sum = scores[0] + scores[1] + \ scores[2] + scores[3] + \ scores[4]; 上述程式碼就是各陣列元素相加的運算式。
13
7-2-1 宣告一維陣列-陣列的初值 在前面的陣列是使用指定敘述指定陣列元素值,C語言的陣列也可以在宣告時用時指定陣列初值,例如:宣告整數的一維陣列,如下所示: int tips[] = {100, 200, 500};
14
7-2-2 使用迴圈存取陣列元素-走訪 使用迴圈走訪整個陣列計算總和,例如:使用for迴圈顯示陣列的每一個元素和計算總和,如下所示:
for ( i=0; i < LENGTH; i++) { sum += scores[i]; printf("成績: %.3f\n", scores[i]); }
15
7-2-2 使用迴圈存取陣列元素-活用常數 常數LENGTH是陣列大小,這是定義在程式開頭的常數,如下所示:
#define LENGTH 5 #define指令定義常數LENGTH,換句話說,陣列只需使用常數就可以宣告陣列大小,如下所示: double scores[LENGTH];
16
7-3 二維與多維陣列的處理 7-3-1 多維陣列的宣告 7-3-2 巢狀迴圈存取多維陣列
17
7-3 二維與多維陣列的處理 多維陣列是指「二維陣列」(Two-dimensional Arrays)含以上維度的陣列,屬於一維陣列的擴充,如果將一維陣列想像成一度空間的線,二維陣列就是一個二度空間,也就是平面。 在日常生活中,二維陣列的應用非常的廣泛,只要屬於平面的各式表格,都可以轉換成二維陣列來表示,例如:月曆、功課表等。如果繼續擴充二維陣列,還可以建立三維、四維等更多維的陣列。
18
7-3-1 多維陣列的宣告-說明 C語言支援二維陣列或多維陣列,筆者準備以二維陣列為例,二維陣列的宣告只是多一個「[]」符號的維度,如下所示: int scores[3][2] = { { 54, 68 }, { 67, 78 }, { 89, 93 } }; 上述程式碼宣告3x2的二維陣列scores,並且指定元素的初值,不同於一維陣列,二維以上的陣列指定初值一定需要指定其大小。
19
7-3-1 多維陣列的宣告-圖例 二維陣列的第一維共有三個元素,每一個元素是一個一維陣列{54, 68}、{67, 78}和{89, 93},即3個一維陣列,每個一維陣列擁有二個元素,總共有3x2等於6個元素,如下圖所示:
20
7-3-1 多維陣列的宣告-使用指定敘述 先宣告一個二維陣列,如下所示: 然後使用指定敘述指定二維陣列的元素值,如下所示:
int scores[3][2]; 然後使用指定敘述指定二維陣列的元素值,如下所示: scores[0][0] = 54; scores[0][1] = 68; scores[1][0] = 67; scores[1][1] = 78; scores[2][0] = 89; scores[2][1] = 93; 同樣的方式,可以將它擴充成多維陣列,如下所示: int scores[3][2][3]; int tips[4][4][5][6];
21
7-3-2 巢狀迴圈存取多維陣列 二維陣列相當於是多個一維陣列的組合,因為一個for迴圈可以走訪一維陣列的元素,換句話說,2層的巢狀迴圈就可以存取二維陣列。 例如:使用for的巢狀迴圈計算二維陣列每一個元素的值,這就是九九乘法表的值,如下所示: for ( i=0; i < LEN; i++) for ( j=0; j < LEN; j++) tables[i][j] = (i+1)*(j+1);
22
7-4 陣列的函數參數-原型宣告 C語言的陣列如同基本資料型態的變數,一樣可以作為函數的參數,不過基本資料型態的變數是傳值呼叫,陣列的參數則是傳址呼叫。 如果函數的參數是陣列,在函數的原型宣告可以使用陣列或指標方式表示,如下所示: void max(int [], int); void max(int *, int);
23
7-4 陣列的函數參數-函數參數 C語言的函數如果傳遞陣列一定需要額外的參數傳遞陣列尺寸,以此例的第2個參數是陣列大小,如下所示:
void max(int values[], int len) { …… } max()函數一共有2個參數,第1個是陣列,第2個整數就是陣列大小,因為陣列是使用傳址呼叫,如果函數中的程式碼更改陣列元素值,同時也會更改呼叫函數傳入的陣列元素。
24
7-5 指標與記憶體位址 7-5-1 指標變數的使用 7-5-2 指標的參數傳遞 7-5-3 指向指標的指標變數
25
7-5 指標與記憶體位址 「指標」(Points)屬於C語言的低階程式處理功能,可以直接存取記憶體位址,事實上,大部分高階程式語言並沒有提供此功能,這也是為什麼C語言稱為中階語言的原因。 C語言的指標變數是指其變數內容不是字元或數值等基本資料型態,而是其它變數的「位址」,換句話說,指標變數單獨存在並沒有意義,因為它的值是其它變數的位址,通常都需先宣告其它變數,如此才能取得指標變數的值,即指向變數的儲存位址。
26
7-5-1 指標變數的使用-說明 在指定敘述「=」等號左邊的變數是左值(Lvalue),即取得變數的位址,「指標變數」(Pointer Variables)是一種變數,其變數值就是變數在左值的「位址」,如下圖所示:
27
7-5-1 指標變數的使用-宣告 指標變數的宣告和基本資料型態變數的宣告稍有不同,其宣告格式,如下所示:
資料型態 *變數名稱; 指標變數宣告和變數宣告只差變數名稱前的「*」星號,簡單的說,這個變數是指向宣告資料型態的指標變數,例如:指向整數的指標變數宣告,如下所示: int *ptr;
28
7-5-1 指標變數的使用-取得變數位址 指標變數的目的是指向其它變數的位址,例如:宣告一個整數變數和指標變數,如下所示:
int j; int *ptr; 程式碼的指標變數和變數都擁有相同型態,可以將指標變數ptr指向變數j的位址,取得變數位址是使用單運算元的「&」取址運算子,如下所示: ptr = &j;
29
7-5-1 指標變數的使用-圖例
30
7-5-1 指標變數的使用-初值 指標變數也可以在宣告時指定初值,此時取得位址的變數一定是在指標變數前宣告,如下:
int i; int *ptr1 = &i; 程式碼先宣告整數變數i,然後是指標變數ptr1,指標變數的初值是變數i的位址。因為C語言的指標變數並沒有預設值,為了避免程式錯誤,例如:尚未指向變數位址就使用指標變數,可以在宣告時將它指定成NULL常數,如下所示: int *ptr = NULL;
31
7-5-1 指標變數的使用-取得指標變數所指的變數值
指標變數之所以稱為指標,這是因為指標變數的值是指向其它變數的位址,換句話說,指標變數對於程式設計者來說,其意義不在指標變數本身,而是在它指向的那一個變數值。 在C程式取得指標變數指向的變數值是使用單運算元的「*」星號運算子,稱為「取值」(Indirection)或「解參考」(Dereferencing)運算子,例如:ptr是指向整數變數j的指標變數,*ptr就是變數j的值,如下所示: printf("*ptr :位址%p的值=%d\n", ptr, *ptr);
32
7-5-1 指標變數的使用-程式範例:Ch7-5-1.c圖例
33
7-5-2 指標的參數傳遞 C語言的傳址呼叫就是指標變數,例如:取得陣列元素最大值的函數max()原型宣告,如下所示:
void max(int *, int, int *); 函數一共擁有3個參數,第1和第3個參數都是整數的指標變數,不過第1個參數是陣列,第3個參數是整數變數,同樣是指標變數,它可能是陣列,也有可能只是一個變數,全憑呼叫時傳遞的參數而定。
34
7-5-3 指向指標的指標變數-說明 指向指標的指標變數就是說這個指標變數是指向其它的指標變數。首先我們宣告一個整數和指標變數,如下所示:
int i = 5; int *ptr = &i; 再宣告一個指向指標的指標變數,如下所示: int **ptr1 = &ptr; 再宣告一個指標變數,這是指向指標的指標變數,如下所示: int ***ptr2 = &ptr1;
35
7-5-3 指向指標的指標變數-圖例1
36
7-5-3 指向指標的指標變數-取值運算 上述圖例的各指標變數的取值運算,分別就是指向變數的值,如下所示:
*ptr2; /* 值為ptr1的位址 */ *ptr1; /* 值為ptr的位址 */ *ptr; /* 值為 5 */
37
7-6 指標與陣列 7-6-1 指標與陣列 7-6-2 指標的運算 7-6-3 指標與二維陣列 7-6-4 指標陣列
38
7-6-1 指標與陣列-說明 C語言的陣列在記憶體配置上是一個連續的記憶體空間,程式碼使用索引值存取陣列元素,如果轉換成指標變數,只需將指標變數指向陣列的第1個元素,元素值存取的操作就可以使用指標運算來完成。 例如:C程式宣告一個大小有6個元素的整數陣列array,如下所示: #define LEN 6 int array[LEN] = {1, 23, 33, 17, -40, 100};
39
7-6-1 指標與陣列-指向陣列的第一個元素1 現在就可以宣告指標變數指向陣列的第一個元素,如下所示:
int *ptr = array; 程式碼宣告指標變數ptr,其初值是陣列名稱,C語言的陣列名稱就是陣列第一個元素的位址,陣列名稱本身是一個指標變數,如下圖所示:
40
7-6-1 指標與陣列-指向陣列的第一個元素2 ptr指向陣列的第一個元素,如果使用取址運算子「&」,其程式碼如下所示:
ptr = &array[0]; 程式碼使用取址運算子取得陣列第一個元素的位址,不同於陣列名稱是指標,陣列每一個元素array[i]是變數值,同理,可以取得最後一個陣列元素的位址,如下所示: ptr = &array[LEN-1];
41
7-6-1 指標與陣列-使用指標存取陣列元素1 在取得陣列第一個元素的位址後,一共有2種方法存取陣列元素的值,如下所示:
for ( i = 0; i < LEN; i++ ) printf("ptr+%d=%d ", i, *(ptr+i)); for迴圈使用指標走訪陣列元素,指標運算ptr+i計算出下一個元素的位址,陣列元素值使用取值運算*(ptr+i) 。
42
7-6-1 指標與陣列-使用指標存取陣列元素2
43
7-6-1 指標與陣列-使用指標存取陣列元素3 第2個方法是使用指標變數的遞增運算ptr++,如下所示:
for ( i = 0; i < LEN; i++ ) printf("ptr+%d=%d ", i, *ptr++); for迴圈使用指標走訪陣列元素,指標運算ptr++可以移到下一個元素,元素值是取值運算*(ptr++),這個方法會真正移動指標變數的位址,等到執行完迴圈,指標變數ptr是指向陣列的最後一個元素。
44
7-6-2 指標的運算-說明1 指標變數的基本運算主要是記憶體位址位移的加減運算和指標變數位址是否相等的比較運算。程式範例是使用整數陣列測試指標變數指向位址的位移,如下所示: #define LEN 8 int array[LEN] = {0,1,2,3,4,5,6,7}; 程式碼宣告整數陣列array且指定初值,一共有8個元素,然後宣告指標變數ptr,如下所示: int *ptr = array;
45
7-6-2 指標的運算-說明2 ptr指標變數,其值是陣列第一個元素的位址。指標變數可以使用指定敘述,如下所示:
ptr1 = ptr; 程式碼的ptr1和ptr指向同一個位址,即陣列的第一個元素,如下圖所示:
46
7-6-2 指標的運算-指標變數的加法 指標變數的加法運算,如下所示:
ptr1 = ptr1 + 5; 程式碼加常數值5,也就是位移5次,總共往後移動5*4=20個位元組,ptr1指向陣列索引5的元素,如下圖所示:
47
7-6-2 指標的運算-指標變數的減法1 指標變數的減法是往前位移到前一個資料型態變數位址,以陣列來說即前一個元素,首先取得陣列最後一個元素的位址,如下所示: ptr1 = &array[LEN-1]; 指標變數的減法運算,如下所示: ptr = ptr1 - 3; 程式碼因為ptr1是指向陣列最後一個元素,往前位移3個元素,ptr指向陣列索引4元素的位址。 同樣的,兩個指標變數也可以相減,如下所示: i = ptr1 - ptr; j = ptr - ptr1;
48
7-6-2 指標的運算-指標變數的減法2 ptr1指向陣列的最後一個元素,ptr1-ptr = 3表 示ptr1是在ptr之後的3個元素,而ptr – ptr1 = -3表示ptr是ptr1之前的3個元素,如下圖所示:
49
7-6-2 指標的運算-指標變數的比較 指標變數也可以比較其位址值,所以一樣也可以使用比較運算子執行ptr1 == ptr或ptr1 < ptr等條件運算,如下所示: for ( i = 0; i < LEN && ptr1 > ptr; i++ ) if ( ptr1 > ptr ) { …… ptr++; ptr1--; }
50
7-6-3 指標與二維陣列-說明 C語言的二維陣列也可以使用指標存取陣列元素,例如:宣告二維整數陣列tables,如下所示:
#define ROWS 4 #define COLS 5 int tables[ROWS][COLS];
51
7-6-3 指標與二維陣列-第一種方法 首先宣告指標指向陣列的第一個元素,如下: 接著使用指標運算取得每一個陣列元素,如下:
int *ptr; ptr = tables; 接著使用指標運算取得每一個陣列元素,如下: for ( i=0; i < ROWS; i++) { for ( j=0; j < COLS; j++) printf("%d*%d=%2d ", (i+1), (j+1), *(ptr+(i*COLS)+j) ); printf("\n"); }
52
7-6-3 指標與二維陣列-第二種方法 使用tables陣列名稱的指標變數,其運算式如下:
*(*(tables + i) + j) tables是二維陣列,可以將它視為指向指標的指標變數,首先看中間括號部分,如下所示: *(tables+i) 取值運算取得的是tables[0][0](tables+0)、tables[1][0](tables+1)、tables[2][0](tables+2)和tables[3][0](tables+3)各列第一個陣列元素的位址。 二維陣列的每一列就是一個一維陣列,在取得每列第一個元素的位址後,只需加上位移量j,就可以取得陣列元素值。
53
7-6-4 指標陣列-說明 「指標陣列」(Arrays of Pointer)是指陣列的每一個元素都是一個指標變數,也就是說陣列元素的值是指向其它變數的位址。
54
7-6-4 指標陣列-宣告 指標陣列的宣告方式類似指標變數,如下所示:
#define ROWS 4 int *tables[ROWS]; 程式碼宣告指標陣列tables,一共擁有4個元素,每一個元素是一個整數的指標變數,可以指向整數或一維陣列,如果都是指向5個元素的一維陣列,相當於宣告4x5的二維陣列。
55
7-6-4 指標陣列-圖例
56
7-6-4 指標陣列-存取1 現在指標陣列tables的元素是指向陣列values元素的位址,使用values[i]存取陣列values元素,也可以使用指標陣列tables,如下所示: value = *tables[i]; 程式碼使用取值運算來取得values陣列元素值。 因為指標陣列相當於是一個指向指標的指標變數,先指向指標陣列,然後再指向真正的變數,所以可以宣告指向指標的指標變數ptr,如下: int **ptr = &tables;
57
7-6-4 指標陣列-存取2 使用指標運算存取指標陣列的元素,如下所示:
for ( i=0; i < ROWS; i++) { printf("*tables[%d]=%2d ", i, *tables[i]); printf("**(tables+%d)=%2d ", i, **(tables + i)); printf("**(ptr+%d)=%2d ", i, **(ptr + i)); } for迴圈可以走訪指標陣列,然後使用*tables[i]、**(tables + i)和**(ptr + i)取得陣列元素的值。
58
7-7 指向函數的指標 7-7-1 函數指標 7-7-2 函數指標的函數參數 7-7-3 void資料型態的指標
59
7-7 指向函數的指標 C語言的函數並不是變數,不過C語言的指 標還是可以指向函數的指標(Pointers to Functions),然後使用指標的取值運算來 執行函數。
60
7-7-1 函數指標-語法 如同指標變數需要指向變數,函數指標也需要先有函數來指向其函數位址,例如:函數max()的原型宣告,如下所示:
int max(int, int); 接著就可以宣告函數指標,其宣告語法,如下所示: 資料型態 (*函數指標名稱) (函數的參數列); 資料型態與函數傳回值的資料型態相同,在函數指標名稱前一樣擁有「*」星號表示它是指標,最後是和指向函數相同的參數列宣告。
61
7-7-1 函數指標-實例 以函數max()為例的函數指標,如下所示:
int (*ptr) (int, int) = max; 程式碼就是宣告指向函數的函數指標ptr。現在我們可以使用指標的取值運算執行函數,如下所示: (*ptr)(a, b); 程式碼使用函數指標呼叫函數,後面括號是函數傳遞的參數值。
62
7-7-2 函數指標的函數參數-說明 函數指標也可以作為其它函數的參數,例如:C程式擁有2個函數的原型宣告max()和min(),如下所示:
int max(int, int); int min(int, int); 2個函數的原型宣告只有函數名稱不同,函數參數列和傳回值都相同。 函數compare(),內含函數指標用來指向上述2個函數max()和min(),如下所示: int compare(int, int, int(*) (int,int));
63
7-7-2 函數指標的函數參數-呼叫 在函數compare()的程式區塊就可以呼叫參數的函數,如下所示:
int compare(int a, int b, int (*ptr) (int, int)) { return (*ptr)(a, b); } 函數的程式區塊以參數a和b來呼叫函數指標的函數,呼叫compare()函數的方式,如下所示: result = compare(a, b, max); result = compare(a, b, min);
64
7-7-3 void資料型態的指標-指向void的指標(Pointers to void)
C語言的void指標屬於「通用型指標」(Generic Pointers),任何資料型態的指標都可以先轉換成void型態的指標,再成功轉換回原指標的型態。 如果在函數使用void指標的參數,可以使用任何資料型態的指標作為傳遞的參數。 如果是指定敘述,指標變數可以指定成void指標,void指標也可以指定成任何型態的指標,任何型態的指標也可以和void指標比較。
65
7-7-3 void資料型態的指標-在函數指標使用void指標1
C程式擁有2個函數的原型宣告numcmp()和chrcmp(),如下所示: int numcmp(int*, int*); int chrcmp(char*, char*); 2個函數的原型宣告中,函數參數列分別是不同資料型態的int*和char*指標。現在筆者準備建立函數compare(),內含函數指標用來指向上述2個函數numcmp()和chrcmp(),如下所示: int compare(void *, void *, int(*) (void *, void *));
66
7-7-3 void資料型態的指標-在函數指標使用void指標2
在函數compare()的程式區塊就可以呼叫參數的函數,如下所示: int compare(void *a, void *b, int (*comp) (void *, void *)) { return (*comp)(a, b); } 函數的程式區塊以參數void指標a和b來呼叫函數指標的函數,呼叫compare()函數的方式,如下: result = compare((void *)ptr, (void *)ptr1, (int (*) (void *, void *))numcmp); result = compare((void *)ptr2, (void *)ptr3, (int (*) (void *, void *))chrcmp);
Similar presentations