Download presentation
Presentation is loading. Please wait.
1
第7章 指標 7-1 指標的基礎 7-2 指標變數的使用 7-3 指標運算 7-4 指標與陣列 7-5 指向函數的指標
2
7-1 指標的基礎-說明 「指標」(Points)是C語言的低階程式處理功能,可以直接存取記憶體位址,事實上,大部分高階程式語言並沒有提供此功能,這也是為什麼C語言稱為中階語言的原因。 C語言的指標是一種變數,其變數內容不是字元或數值等基本資料型態的值,而是其他變數的「位址」,所以,指標變數單獨存在並沒有意義,因為它的值是指向其他變數的位址,能夠讓我們間接取得其他變數的值。
3
7-1 指標的基礎-圖例 在C語言指定敘述的「=」等號左邊變數是左值(Lvalue),即取得變數的位址屬性,「指標變數」(Pointer Variables)是一種變數,其值是變數在指定敘述左值的「位址」,如下圖所示:
4
7-2 指標變數的使用 7-2-1 宣告指標變數與初始值 7-2-2 指標運算子 7-2-3 指標的參數傳遞 7-2-4 指向指標的指標變數
5
7-2-1 宣告指標變數與初始值-宣告指標變數 C語言指標變數的宣告語法格式,如下所示: 資料型態 *變數名稱;
變數是指向宣告資料型態的指標變數。例如:指向int整數的指標變數宣告,如下所示: int *ptr; 程式碼宣告一個指向整數的指標變數ptr,換句話說,它能指向一個宣告成int整數資料型態的變數。其他資料型態的指標變數宣告,如下所示: char *ptr1; float *ptr2; double *ptr3;
6
7-2-1 宣告指標變數與初始值-指標變數的初始值(說明)
指標變數可以在宣告時指定初始值,而取得位址的變數一定是在指標變數前宣告,如下所示: int b = 30; int *ptr = &b; 程式碼先宣告整數變數b,然後宣告指標變數ptr,指標變數的初始值是使用「&」取址運算子取得變數b的位址。
7
7-2-1 宣告指標變數與初始值-指標變數的初始值(圖例)
指標變數ptr的值0022FF74是整數變數b的記憶體位址,指標變數ptr1是NULL指標,NULL常數值是0,如下圖所示:
8
7-2-1 宣告指標變數與初始值-指標變數的預設值
C語言的指標變數並沒有預設值,為了避免程式執行產生錯誤,例如:尚未指向變數位址就使用指標變數,可以在宣告時將它指定成NULL常數,如下所示: int *ptr1 = NULL; 指標稱為NULL指標,如此在使用前就可以使用if條件判斷指標變數是否已經指向其他變數,如下所示: if ( ptr1 == NULL ) { }
9
7-2-2 指標運算子-&取址運算子 C語言提供兩種指標運算子(Pointer Operators)可以取得指標變數所需的變數位址和指標變數所指的變數值。 「&」取址運算子(二元運算子「&」是位元運算子AND)是一種單運算元運算子,可以取得運算元變數的位址,如下所示: ptr = &a; 上述程式碼是將指標變數ptr指定成變數a的記憶體位址,變數ptr的值就是變數a的記憶體位址。
10
7-2-2 指標運算子-*取值運算子(說明) 「*」星號運算子(二元運算子「*」是乘法)相對於取址運算子,稱為「取值」(Indirection)或「解參考」(Dereferencing)運算子,這也是一種單運算元運算子,可以取得運算元指標變數的變數值。 例如:ptr是指向整數變數a的指標變數,*ptr就是變數a的值,如下所示: b = *ptr;
11
7-2-2 指標運算子-*取值運算子(圖例) ptr是變數a的位址,*ptr是變數a的值,所以變數b將指定成變數a的值,如下圖所示:
12
7-2-3 指標的參數傳遞 C語言的傳址呼叫是使用指標變數。例如:取得陣列元素最大值的maxElement()函數,其原型宣告如下所示:
void maxElement(int *, int *); 函數擁有2個參數是整數的指標變數,第1個參數是陣列data[] ,第2個參數是使用「&」取址運算子取得變數index的位址,如下所示: maxElement(data, &index);
13
7-2-4 指向指標的指標變數-說明 指向指標的指標變數是指這個指標變數是指向其他的指標變數。宣告整數和指標變數,如下所示:
int a = 15; int *ptr = &i; 程式碼的指標變數ptr是指向變數a。接著宣告指向指標的指標變數,如下所示: int **ptr1 = &ptr; ptr1有2個星號,第1個星號指出變數是指標變數,第2個星號表示是指向其他的指標變數。
14
7-2-4 指向指標的指標變數-指向指標的指標變數
接著再宣告一個指標變數,這是指向指標的指標變數,如下所示: int ***ptr2 = &ptr1;
15
7-2-4 指向指標的指標變數-取值運算 指標變數ptr2指向ptr1,ptr1指向ptr,ptr指向變數a,各指標變數的取值運算,分別就是指向變數的值,如下所示: *ptr2; /* 值為ptr1的位址 */ *ptr1; /* 值為ptr的位址 */ *ptr; /* 值為 15 */
16
7-3 指標運算 7-3-1 指標的指定敘述 7-3-2 指標的算術運算 7-3-3 指標的比較運算
17
7-3-1 指標的指定敘述 如同其他C語言的變數,指標變數也可以在指定敘述的右邊,將指標變數值指定給其他指標變數,如下所示:
int *ptr1, *ptr = &a; ptr1 = ptr; 指標變數ptr1和ptr擁有相同值,也就是相同變數a記憶體位址的值。
18
7-3-2 指標的算術運算-說明1 指標變數的算術運算是記憶體位址位移的遞增、遞減和加減運算。在本節程式範例是宣告一維陣列a[]來測試指標的算術運算,如下所示: int a[]= { 1, 2 , 3, 4, 5}; 程式碼宣告整數陣列且指定初值。C編譯程式會配置連續記憶體空間來儲存這些陣列元素。
19
7-3-2 指標的算術運算-說明2 我們可以宣告指標變數ptr和ptr1來指向陣列第1個最後1個元素的位址,如下所示:
int *ptr = &a[0]; int *ptr1 = &a[4];
20
7-3-2 指標的算術運算-指標變數的遞增和遞減運算(說明)
指標變數可以使用遞增和遞減運算來移動指標指向的位址,首先是遞增運算,如下所示: ptr++; 例如:目前指標變數ptr的值為0022FF50,下一個是0022FF54。接著執行遞減運算,如下所示: ptr--; 例如:目前指標變數ptr的值為0022FF54,前一個是0022FF50。
21
7-3-2 指標的算術運算-指標變數的遞增和遞減運算(圖例)
22
7-3-2 指標的算術運算-指標變數的加法運算 指標變數的加法運算可以讓指標一次就向下位移常數值的整個區段,如下所示:
ptr = ptr + 3;
23
7-3-2 指標的算術運算-指標變數的減法運算 指標變數減法的位移方向和加法相反,指標是往前位移整個區段的位址,如下所示:
ptr = ptr - 2;
24
7-3-2 指標的算術運算-指標變數相減算 C語言的兩個指標變數也可以相減,如下所示: i = ptr1 - ptr;
j = ptr - ptr1; 上述程式碼是指標變數相減,結果是2個指標變數間相差的資料型態個數,因為目前ptr指向變數a2,ptr1指向變數a5,ptr1-ptr = 3表示ptr1是在ptr之後的3個元素,而ptr – ptr1 = -3表示ptr是ptr1之前的3個元素。
25
7-3-3 指標的比較運算-1 在C程式可以使用關係運算子執行ptr == ptr1或ptr2 > ptr1等條件運算,如下所示:
if ( ptr2 > ptr1 ) printf("ptr2高於ptr1的記憶體位址!\n"); else printf("ptr1高於ptr2的記憶體位址!\n");
26
7-3-3 指標的比較運算-2 if條件是比較指標變數的記憶體位址哪一個比較高,如果指向同一個記憶體位址就表示是相等,如下所示:
if ( ptr == ptr1 ) printf("ptr和ptr1記憶體位址相等!\n");
27
7-4 指標與陣列 7-4-1 指標與陣列 7-4-2 指標與二維陣列 7-4-3 指標陣列
28
7-4-1 指標與陣列-說明 C語言的陣列在記憶體配置上是連續的記憶體空間,除了可以使用索引值存取陣列元素,還可以使用指標變數,將指標變數指向陣列的第1個元素,此後的元素存取操作就是指標的算術運算。例如:在C程式宣告大小6個元素的整數陣列data[],如下所示: #define LEN 6 int data[LEN] = {11, 93, 45, 27, -40, 80};
29
7-4-1 指標與陣列-指向陣列的第1個和最後1個元素(說明)
宣告指標變數指向陣列的第1個元素,如下所示: int *ptr; ptr = data; C語言的陣列名稱就是陣列第1個元素的位址,如果使用取址運算子「&」,其程式碼如下所示: ptr = &data[0]; 取得最後1個陣列元素的位址,如下所示: ptr = &data[LEN-1];
30
7-4-1 指標與陣列-指向陣列的第1個和最後1個元素(圖例)
在C語言的陣列名稱就是陣列第1個元素的位址,換句話說,陣列名稱本身是一個指標變數,ptr指向陣列的第1個元素,如下圖所示:
31
7-4-1 指標與陣列-使用指標存取陣列元素(說明)
在取得陣列第1個元素的位址後,共有2種方法存取每一個陣列元素值。第一種是使用指標加法,如下所示: for ( i = 0; i < LEN; i++ ) printf("ptr+%d=%d ", i, *(ptr+i)); 第二個方法是使用指標的遞增運算ptr++,如下所示: printf("ptr+%d=%d ", i, *ptr++);
32
7-4-1 指標與陣列-使用指標存取陣列元素(圖例)
for迴圈使用指標走訪陣列元素,指標加法ptr+i計算出下一個元素位址,陣列元素使用取值運算*(ptr+i)取出元素值,如下:
33
7-4-2 指標與二維陣列-說明 C語言的二維陣列也可以使用指標來存取陣列元素。例如:宣告一個二維整數陣列tables[][],如下所示:
#define ROWS 4 #define COLS 5 int tables[ROWS][COLS];
34
7-4-2 指標與二維陣列-方法一:視為一維陣列來存取
二維陣列配置的記憶體空間是將每一列結合起來的連續記憶體位址,tables[][]二維陣列如同是一個4列合一的一維陣列。可以宣告指標變數ptr指向陣列的第1個元素,如下所示: int *ptr; ptr = &tables[0][0]; 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"); }
35
7-4-2 指標與二維陣列-方法二:使用二維陣列名稱(說明)
二維陣列名稱tables本身是一個指標變數,我們可以使用指標運算式取得陣列的每一個元素,如下所示: *(*(tables + i) + j) 上述tables是二維陣列,可以將它視為是一個指向指標的指標變數,首先看中間括號部分的運算式,如下所示: *(tables+i) 上述指標運算可以當作是第一欄tables[ROWS][0]的指標運算。
36
7-4-2 指標與二維陣列-方法二:使用二維陣列名稱(圖例)
因為是指向指標的指標變數,取值運算取得的是tables[0][0](tables+0)、tables[1][0](tables+1)、tables[2][0](tables+2)和tables[3][0](tables+3)各列第1個陣列元素的位址,如下圖所示:
37
7-4-3 指標陣列-指標陣列的宣告 「指標陣列」(Arrays of Pointer)是一個陣列,只是每一個元素都是一個指標變數,也就是說陣列元素的值是指向其他變數的位址。 指標陣列的宣告方式類似指標變數,如下所示: #define ROWS 4 int *tables[ROWS]; 上述程式碼宣告指標陣列tables[],一共擁有4個元素,每一個元素是一個整數的指標變數,可以指向整數或一維陣列,如果都是指向5個元素的一維陣列,相當於宣告4X5的二維陣列。
38
7-4-3 指標陣列-另一個一維陣列 因為指標陣列並不能指定初值,所以筆者準備建立另一個一維陣列data[],用來作為指標陣列指向的變數,如下所示: int data[ROWS] = {20, 14, 35, 59}; 上述一維陣列已經指定初值,所以指標陣列的每一個指標可以指向data[]陣列對應的元素,如下所示: for ( i=0; i < ROWS; i++) tables[i] = &data[i];
39
7-4-3 指標陣列-圖例 for迴圈指定指標陣列各元素的初值,執行後的圖例,如下圖所示:
40
7-4-3 指標陣列-指標陣列的存取1 現在指標陣列tables[]的元素是指向陣列data[]元素的位址,除了可以使用data[i]存取陣列data[]的元素,也可以使用指標陣列tables[],如下所示: value = *tables[i]; 指標陣列相當於是指向指標的指標變數,先指向指標陣列後,各元素再指向真正的變數,所以可以宣告指向指標的指標變數ptr來指向指標陣列的第1個元素或直接使用tables陣列名稱的指標,如下所示: int **ptr = tables;
41
7-4-3 指標陣列-指標陣列的存取2 因為已經指向各指標陣列的第1個元素,所以可以使用指標運算存取指標陣列的元素,如下所示:
for ( i=0; i < ROWS; i++) { printf("*tables[%d]=%d ", i, *tables[i]); printf("**(tables+%d)=%d ", i, **(tables + i)); printf("**(ptr+%d)=%d ", i, **(ptr + i)); } 上述for迴圈可以走訪指標陣列,然後使用*tables[i]、**(tables + i)和**(ptr + i)取得陣列元素的值。
42
7-5 指向函數的指標 7-5-1 函數指標 7-5-2 函數指標的函數參數 7-5-3 void資料型態的指標
43
7-5-1 函數指標-語法 函數指標也需要先有函數來指向函數的位址,例如:函數maxParam()的原型宣告,如下所示:
int maxParam(int, int); 接著宣告函數指標,其宣告語法,如下所示: 資料型態 (*函數指標名稱) (函數的參數列); 資料型態與函數傳回值的資料型態相同,在函數指標名稱前一樣擁有「*」星號表示它是指標,而且一定需要括號,之後是和指向函數相同的參數列宣告,參數列只需參數型態即可。
44
7-5-1 函數指標-範例 例如:函數maxParam()為例的函數指標,如下所示:
int (*ptr) (int, int) = maxParam; 上述程式碼宣告指向函數maxParam()的函數指標ptr。現在我們可以使用指標的取值運算來呼叫函數,如下所示: (*ptr)(a, b); 上述程式碼的括號中是使用函數指標的取值運算來呼叫函數,後面括號是函數傳遞的參數列。
45
7-5-2 函數指標的函數參數-1 函數指標不只可以在程式碼呼叫函數,還可以作為其他函數的參數,其目的是讓函數能夠使用函數指標的參數來決定呼叫哪一個函數。 例如:C程式擁有2個函數的原型宣告maxParam()和minParam(),如下所示: int maxParam(int, int); int minParam(int, int); 現在筆者準備建立函數compare(),內含函數指標指向上述2個函數maxParam()和minParam(),如下所示: int compare(int, int, int(*) (int,int));
46
7-5-2 函數指標的函數參數-2 在函數compare()的程式區塊可以使用參數來呼叫函數,如下所示:
int compare(int a,int b,int (*ptr) (int, int)) { return (*ptr)(a, b); } 函數的程式區塊是以參數a和b呼叫函數指標指向的函數。呼叫compare()函數的方式,如下所示: result = compare(x, y, maxParam); result = compare(x, y, minParam);
47
7-5-3 void資料型態的指標-指向void的指標
C語言的void指標屬於「通用型指標」(Generic Pointers),任何資料型態的指標都可以先轉換成void型態的指標,再成功轉換回原來指標的資料型態。如果函數使用void指標的參數,換句話說,我們可以使用任何資料型態的指標作為傳遞的參數。 如果是使用在指定敘述,指標變數可以指定成void指標,void指標也可以指定成任何型態的指標,任何型態的指標也可以和void指標比較,簡單的說,在指定敘述的指標運算式可以同時使用void指標或其他資料型態的指標。
48
7-5-3 void資料型態的指標-在函數指標使用void指標1
現在筆者準備在函數指標使用void指標,以便使用不同資料型態指標的函數參數。例如:C程式擁有2個函數的原型宣告numcmp()和chrcmp(),如下所示: int numcmp(int*, int*); int chrcmp(char*, char*); 現在筆者準備建立函數compare(),內含函數指標用來指向上述2個函數numcmp()和chrcmp(),如下所示: int compare(void *, void *, int(*) (void *, void *));
49
7-5-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