620 likes | 701 Views
資料結構. 第 3 章 指標與字串 (Pointers and Strings). 第 3 章 課 程大綱. 3-1 C 語言的 指標 3-2 指標與 陣列 3-3 指標與 結構 3-4 指標與 字串 3-5 指標的 應用 - 字串處理. 3-1 C語言的指標. 3-1-1 指標變數 (Pointer Variable) 的使用 3-1-2 指向指標 的 指標變數. 3-1 C語言的指標. C 語言的 「指標」( Points ) 屬於一種低階的程式處理功能 可以直接存取 記憶體位址
E N D
資料結構 第3章 指標與字串(Pointers and Strings)
第3章 課程大綱 • 3-1 C語言的指標 • 3-2 指標與陣列 • 3-3 指標與結構 • 3-4 指標與字串 • 3-5 指標的應用- 字串處理
3-1 C語言的指標 • 3-1-1 指標變數(Pointer Variable)的使用 • 3-1-2 指向指標的指標變數
3-1 C語言的指標 • C語言的「指標」(Points)屬於一種低階的程式處理功能 • 可以直接存取記憶體位址 • 指標變數的變數內容並不是字元或數值等基本資料型態的值,而是其它變數的「位址」(Address)。 • 換句話說,單獨存在的指標變數並沒有意義,因為它的值是其它變數的位址,程式需先宣告其它變數,才能取得指標變數的值,即指向其它變數的儲存位址。
3-1-1 指標變數的使用-說明 • C語言的「指標變數」(Pointer Variables)是一種變數,其變數值是其它變數的位址(Address),如下圖所示: 間接索引
3-1-1 指標變數的使用-宣告 • 指標變數的宣告和基本資料型態變數的宣告稍有不同,其宣告格式,如下所示: 資料型態 *變數名稱; • 指標變數宣告和變數宣告只差變數名稱前的「*」星號,簡單的說,這個變數是指向宣告資料型態的指標變數, • 例如:指向整數int的指標變數宣告,如下所示: int *ptr;
3-1-1 指標變數的使用-取得變數位址 • 將指標變數ptr指向變數j的位址 • 取得變數位址是使用單運算元的「&」取址運算子,如下所示: ptr = &j; //取得變數 J 的位置
3-1-1 指標變數的使用-初值 • 指標變數也可以在宣告時指定初值,不過取得位址的變數一定需要在指標變數前宣告,如下所示: int i; int *ptr1 = &i; • 上述程式碼先宣告整數變數i,然後是指標變數ptr1,指標變數的初值是變數i的位址。 • 因為C語言的指標變數並沒有預設值,為了避免程式錯誤,例如:尚未指向其它變數的位址就使用指標變數,可以在宣告時指定成NULL常數,如下所示: int *ptr = NULL;
3-1-1 指標變數的使用-取得變數值 • 在C程式取得指標變數指向的變數值 • 是使用單運算元的「*」星號運算子,稱為「取值」(Indirection)或「解參考」(Dereferencing)運算子, • 例如:ptr是指向整數變數j的指標變數,*ptr就是變數j的值,如下所示: printf("*ptr :位址%p的值=%d\n", ptr, *ptr); • 上述printf()函數使用%p格式字元顯示指標變數的值,ptr是變數j的位址,*ptr是變數j的值。
程式範例Ch3-1-1.c • 程式目標 • 在C程式中宣告數個變數和指標變數, 然後測試 ”&”取址運算子和 “*”取值運算子 • 執行結果如下 • i=1 位址=0022FF74 • j=2位址=0022FF70 • ptr=0022FF70位址=0022FF6C • *ptr=位址0022FF70的值=2 • ptr1=0022FF74位址=0022FF68 • *ptr1=位址0022FF74的值=1
/* 程式範例: Ch3-1-1.c */ • #include <stdio.h> • #include <stdlib.h> • /* 主程式 */ • int main() { • /* 宣告變數 */ • int i, j; • int *ptr = NULL; • int *ptr1 = &i; /* 指標變數的初值 */ • i = 1; • j = 2; • /* 取得指標變數的值 */ • ptr = &j; • printf("i =%8d 位址=%p\n", i, &i); • printf("j =%8d 位址=%p\n", j, &j); • printf("ptr =%p 位址=%p\n", ptr, &ptr); • printf("*ptr :位址%p的值=%d\n", ptr, *ptr); • printf("ptr1=%p 位址=%p\n", ptr1, &ptr1); • printf("*ptr1:位址%p的值=%d\n", ptr1, *ptr1); • system("PAUSE"); • return 0; • }
3-1-2 指向指標的指標變數-說明 • 指向指標的指標變數是說指標變數是指向其它的指標變數。 • 首先在程式宣告一個整數 i 和指標變數 ptr,如下所示: int i = 5; int *ptr = &i; • 接著再宣告一個指向指標的指標變數ptr1,如下所示: int **ptr1 = &ptr; • 指標變數ptr1共有2個星號,第1個星號指出變數是一個指標變數,第2個星號表示它是指向指標變數,即ptr。 • 接著再宣告指標變數指向指標的指標變數,如下所示: int ***ptr2 = &ptr1; • 指標變數ptr2共有3個星號,第1個星號指出變數是指標變數,後2個星號表示它是指向指標的指標變數,即ptr1。
程式範例Ch3-1-2.c • 程式目標 • 在C程式中宣告指標變數, 指向指標的指標變數和指向指標的指標的指標變數, 然後分別執行取值運算, 以顯示其位址與值!! • 執行結果如下 • i=5 位址=0022FF74 • ptr=0022FF74 位址=0022FF70 • ptr1=0022FF70 位址=0022FF6C • ptr2=0022FF6C 位址=0022FF68 • *ptr=位址0022FF74的值=5 • *ptr1=位址0022FF70的值=0022FF74 • *ptr2=位址0022FF6C的值=0022FF70 • *ptr = 5 • **ptr1 = 5 • ***ptr2 = 5
/* 程式範例: Ch3-1-2.c */ • #include <stdio.h> • #include <stdlib.h> • /* 主程式 */ • int main() { • /* 宣告變數 */ • int i = 5; • int *ptr = &i; • int **ptr1 = &ptr; • int ***ptr2 = &ptr1; • /* 取得指標變數的值 */ • printf("i =%8d 位址=%p\n", i, &i); • printf("ptr =%p 位址=%p\n", ptr, &ptr); • printf("ptr1=%p 位址=%p\n", ptr1, &ptr1); • printf("ptr2=%p 位址=%p\n", ptr2, &ptr2); • printf("*ptr :位址%p的值=%8d\n", ptr, *ptr); • printf("*ptr1:位址%p的值=%p\n", ptr1, *ptr1); • printf("*ptr2:位址%p的值=%p\n", ptr2, *ptr2); • printf("*ptr = %d\n", *ptr); • printf("**ptr1 = %d\n", **ptr1); • printf("***ptr2= %d\n", ***ptr2); • system("PAUSE"); • return 0; • }
3-2 指標與陣列 (Pointers and Arrays) • 3-2-1 指標與陣列 • 3-2-2 指標與二維陣列 • 3-2-3 指標陣列
3-2-1 指標與陣列-說明 • C語言的陣列配置 • 是一塊連續的記憶體空間,然後使用索引值存取陣列元素 • 如果改成指標變數,只需將指標變數指向陣列的第1個元素, • 元素存取就可以使用指標的數運算加和減來完成。
3-2-1 指標與陣列-指向陣列元素 • 宣告指標變數指向陣列的第1個元素,如下所示: int *ptr = array; • C語言的陣列名稱就是陣列第1個元素的位址,因為陣列名稱本身是一個指標變數,如果使用取址運算子「&」,其程式碼如下所示: ptr = &array[0];
3-2-1 指標與陣列-走訪陣列(方法1) • 一共有2種方法存取其它陣列元素的值,如下所示: for ( i = 0; i < LEN; i++ ) printf("ptr+%d=%d ", i, *(ptr+i));
3-2-1 指標與陣列-走訪陣列(方法2) • 第2個方法是使用指標變數的遞增運算ptr++,如下所示: for ( i = 0; i < LEN; i++ ) printf("ptr+%d=%d ", i, *ptr++); • 上述for迴圈使用指標走訪陣列元素,指標運算ptr++移到下一個元素,元素值是取值運算*(ptr++), • 這個方法會真正移動指標變數的位址,等到執行完迴圈,指標變數ptr是指向陣列的最後1個元素。
程式範例Ch3-2-1.c • 程式目標 • 在C程式中宣告一維陣列後, 分別使用陣列索引, 指標運算與遞增, 遞減運算顯示陣列所有元素的值!! • 執行結果如下 • array[0] = 1 array[1] = 23 array[2] = 23 array[3] = 17 array[4] = -40 array[5] =100 • ptr+0=1 ptr+1=23 ptr+2=23 ptr+3=17 ptr+4=-40 ptr+5=100 • ptr-0=100 ptr-1 = -40 ptr-2=17 ptr-3=23 ptr-4=23 ptr-5 =1
ptr = &array[0]; /* 第一個元素 */ for ( i = 0; i < LEN; i++ ) printf("ptr+%d=%d ", i, *ptr++); printf("\n"); ptr = &array[LEN-1]; /* 最後一個元素 */ for ( i = 0; i < LEN; i++ ) printf("ptr-%d=%d ", i, *ptr--); printf("\n"); system("PAUSE"); return 0; } /* 程式範例: Ch3-2-1.c */ #include <stdio.h> #include <stdlib.h> #define LEN 6 /* 主程式 */ int main() { /* 宣告變數 */ int i; /* 建立int陣列且指定初值 */ int array[LEN] = { 1, 23, 33, 17, -40, 100 }; int *ptr = array; /* 第一個元素 */ /* 顯示陣列元素的值 */ for ( i = 0; i < LEN; i++ ) printf("array[%d]=%d ",i,array[i]); printf("\n"); ptr = array; /* 第一個元素 */ for ( i = 0; i < LEN; i++ ) printf("ptr+%d=%d ", i, *(ptr+i)); printf("\n");
3-2-2 指標與二維陣列-範例 • C語言的二維陣列也可以使用指標變數來存取陣列元素,例如:宣告一個二維整數陣列tables[][],如下所示: #define ROWS 4 #define COLS 5 int tables[ROWS][COLS];
3-2-2 指標與二維陣列-方法1 • 視為一維陣列存取,因為二維陣列配置的記憶體空間是將每一列結合起來的連續記憶體位置,如同4列合一的一維陣列,如下所示: int *ptr; ptr = &tables[0][0]; • 指標變數ptr指向陣列第1個元素的位址。 • 接著使用指標運算取得每一個陣列元素,如下所示: for ( i=0; i < ROWS; i++) { //先控制Row在控制COLS for ( j=0; j < COLS; j++) printf("%d*%d=%2d ", (i+1), (j+1), *(ptr+(i*COLS)+j)); printf("\n"); }
3-2-2 指標與二維陣列-方法2 • 第二個方法是使用tables[ ][ ]陣列名稱的指標變數,其運算式如下所示: *(*(tables + i) + j) • tables是二維陣列的名稱,可以將它視為是一個指向指標的指標變數,在中間括號部分的運算式,如下所示: *(tables+i) • 上述指標運算可以當作是第一欄tables[ROWS][0]的指標運算,取值運算取得的是: • tables[0][0](tables+0) • tables[1][0](tables+1) • tables[2][0](tables+2) • tables[3][0](tables+3)
程式範例Ch3-2-2.c • 程式目標 • 在C程式中宣告 2 維陣列儲存部分的九九乘法表, 然後使用指標方式顯示部分的九九乘法表!! • 執行結果如下 • 1*1=1 1*2=2 1*3=3 1*4=4 1*5=5 • 2*1=2 2*2=4 2*3=6 2*4=8 2*5=10 • 3*1=3 3*2=6 3*3=9 3*4=12 3*5=15 • 4*1=4 4*2=8 4*3=12 4*4=16 4*5=20 • 上方是用一維陣列的指標存取, 下方是用二維陣列的指標存取來顯示部分的九九乘法表 • 1*1=1 1*2=2 1*3=3 1*4=4 1*5=5 • 2*1=2 2*2=4 2*3=6 2*4=8 2*5=10 • 3*1=3 3*2=6 3*3=9 3*4=12 3*5=15 • 4*1=4 4*2=8 4*3=12 4*4=16 4*5=20
/* 顯示二維陣列的元素值 */ 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"); } printf("\n"); for ( i=0; i < ROWS; i++) { for ( j=0; j < COLS; j++) printf("%d*%d=%2d ", (i+1), (j+1), *(*(tables + i) + j)); printf("\n"); } system("PAUSE"); return 0; } /* 程式範例: Ch3-2-2.c */ #include <stdio.h> #include <stdlib.h> #define ROWS 4 #define COLS 5 /* 主程式 */ int main() { /* 宣告變數 */ int i, j; /* 建立int的二維陣列 */ int tables[ROWS][COLS]; int *ptr; /* 指定二維陣列的元素值 */ for ( i=0; i < ROWS; i++) for ( j=0; j < COLS; j++) tables[i][j] = (i+1)*(j+1);
/* 程式範例: Ch3-2-2.c-1 */ • /* 九九乘法表*/ • #include <stdio.h> • #include <stdlib.h> • #define ROWS 9 • #define COLS 9 • /* 主程式 */ • int main() { • /* 宣告變數 */ • int i, j; • /* 建立int的二維陣列 */ • int tables[ROWS][COLS]; • int *ptr; • /* 指定二維陣列的元素值 */ • for ( i=0; i < ROWS; i++) • for ( j=0; j < COLS; j++) • tables[i][j] = (i+1)*(j+1); • /* 顯示二維陣列的元素值 */ • 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"); • } • printf("\n"); • for ( i=0; i < ROWS; i++) { • for ( j=0; j < COLS; j++) • printf("%d*%d=%2d ", (i+1), (j+1), *(*(tables + i) + j)); • printf("\n"); • } • system("PAUSE"); • return 0; • }
3-2-3 指標陣列-宣告 • 「指標陣列」(Arrays of Pointer)是指陣列的每一個元素都是一個指標變數 • 也就是說,陣列元素的值都是指向其它變數的位址。 • 指標陣列的宣告方式類似指標變數,如下所示: #define ROWS 4 int *tables[ROWS]; • 上述程式碼宣告指標陣列tables[],一共擁有4個元素,每一個元素是一個整數的指標變數,可以指向整數或一個一維陣列, • 如果都是指向5個元素的一維陣列,相當於是宣告一個4 X 5的二維陣列。
3-2-3 指標陣列-差異 • 指標陣列和二維陣列的差異在 • 指標陣列並不能指定初值, • 二維陣列在宣告後就配置ROWS X COLS個元素,指標陣列只配置ROWS個元素, • 指標陣列的最大好處是每一個指標指向的變數可以是不同長度, • 例如:不同元素分別指向整數或整數陣列。
3-2-3 指標陣列-存取 • 使用指標運算存取指標陣列的元素,如下所示: 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)取得陣列元素的值。
程式範例Ch3-2-3.c • 程式目標 • 在C程式中宣告一個指標陣列和一維陣列, 將指標陣列的每一個元素指向一維陣列的元素, 然後用指標運算顯示出指標陣列的元素值!! • 執行結果如下 • **ptr=&tables[0]; • *tables[0]=10 **(tables+0)=10 **(ptr+0)=10 位址=0022FF50 • *tables[1]=4 **(tables+0)=4 **(ptr+0)=10 位址=0022FF54 • *tables[2]=5 **(tables+0)=5 **(ptr+0)=10 位址=0022FF58 • *tables[3]=19 **(tables+0)=19 **(ptr+0)=10 位址=0022FF5C
/* 程式範例: Ch3-2-3.c */ #include <stdio.h> #include <stdlib.h> #define ROWS 4 /* 主程式 */ int main() { /* 宣告變數 */ int i; int values[ROWS] = { 10, 4, 5, 19 }; /* 建立int的指標陣列 */ int *tables[ROWS]; int **ptr = &tables[0]; /* 指向指標的指標變數 */ /* 指定指標陣列的元素值 */ for ( i=0; i < ROWS; i++) tables[i] = &values[i]; /* 取得陣列元素位址 */ /* 顯示指標陣列的元素值 */ printf("**ptr = &tables[0];\n"); 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)); printf("位址=%p\n", tables[i]); } system("PAUSE"); return 0; • }
3-3 指標與結構-宣告 • 指標也可以指向結構。 • 例如:宣告label結構儲存員工的姓名和年齡,如下所示: struct label { char name[20]; int age; }; • 上述結構擁有2個成員變數 • 因為指標需要指向結構變數的位址,所以需要先宣告結構變數,然後才能建立指向結構的指標,如下所示: struct label worker; struct label *ptr;
3-3 指標與結構-存取 • 接著將結構指標指向結構,如下所示: ptr = &worker; • 結構指標ptr指向結構變數worker的位址, • 換個方式,可以使用指標變數存取結構的成員變數,如下所示: (*ptr).age = 50; • 程式碼相當於是worker.age = 50;。 • C語言提供另一種語法,結構指標可以直接使用「->」運算子存取結構的成員變數,如下所示: ptr->age = 50;
程式範例Ch3-3.c • 程式目標 • 在C程式中宣告結構label儲存員工資料, 然後建立結構變數與指標, 分別使用結構變數與指標來存取成員變數!! • 執行結果如下 • 姓名: 陳會安 • 年齡: 30 • 員工名牌 ------------ • 姓名: 陳會安 • 年齡: 30 • 員工名牌 ------------
/* 主程式 */ int main() { /* 宣告變數 */ struct label worker; struct label *ptr; /* 將結構指標指向結構 */ ptr = &worker; /* 指定結構的成員變數 */ strcpy(worker.name, "陳會安"); ptr->age = 30; printf("員工基本資料\n"); printf("姓名: %s\n", worker.name); printf("年齡: %d\n", worker.age); /* 呼叫函數 */ showlabel(ptr); system("PAUSE"); return 0; } /* 程式範例: Ch3-3.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> /* 結構label的宣告 */ struct label { char name[20]; int age; }; /* 函數: 顯示結構指標的成員變數 */ void showlabel(struct label *ptr) { printf("--------------------\n"); printf("員工名牌\n"); printf("姓名: %s\n", ptr->name); printf("年齡: %d\n", ptr->age); printf("------------------\n"); }
3-4 指標與字串 • 3-4-1 指標與字串 • 3-4-2 字串的指標陣列
3-4 指標與字串 • 在C語言的字串是一種字元陣列,指標運算也一樣適用在字元陣列的字串。 • 例如:宣告一個字元陣列來儲存字串,其宣告如下所示: char line[80]; • 存取字元陣列元素是使用line[0]、line[1]~line[79]來存取,如下所示: line[i] = c; • 在字元陣列的結束加上字元'\0'當作結束字元,如下所示: line[i] = '\0';
3-4-1 指標與字串 • 字串指標是一個char資料型態的指標,可以用來指向字元陣列或字串常數。 • 宣告一個字元陣列的字串,如下所示: #define LEN 16 char str[LEN] = "This is a book."; • 上述字元陣列是一個字串且指定初值,接著宣告指標變數指向此字串,如下所示: char *ptr = str;
程式範例Ch3-4-1.c • 程式目標 • 在C程式中宣告指標變數分別指向字元陣列的字串與字串常數, 然後使用指標運算來複製字串內容!! • 執行結果如下 • str = This is a book. • ptr = This is a book. • ptr1= This is a book. • 將字串str複製到str1: • str1 = This is a book. • ptr1= This is a book.
/* 字串複製的迴圈 */ printf("將字串str複製到str1: \n"); i = 0; ptr1 = str1; while ( *ptr != '\0' ) { *(ptr1+i) = *ptr++; i++; } *(ptr1+i) = '\0'; printf("str1 = %s\n", str1); printf("ptr1 = %s\n", ptr1); system("PAUSE"); return 0; } /* 程式範例: Ch3-4-1.c */ #include <stdio.h> #include <stdlib.h> #define LEN 16 /* 主程式 */ int main() { /* 字元陣列宣告 */ char str[LEN] = "This is a book."; char str1[LEN]; /* 字元指標 */ char *ptr = str; char *ptr1; int i; /* 顯示字串內容 */ ptr1 = "This is a pen."; printf("str = %s\n", str); printf("ptr = %s\n", ptr); printf("ptr1 = %s\n", ptr1);
3-4-2 字串的指標陣列-說明 • C語言的指標陣列最常是應用在字串的指標陣列,如下所示: #define ROWS 4 char *name[ROWS] = { "陳會安", "江小魚", "張無忌", "楊過" };
3-4-2 字串的指標陣列-字串交換 • 使用指標陣列來儲存字串, • 不只可以節省記憶空間,而且因為是指標,如果指標陣列的元素需要交換字串,只需更改指標指向的字串即可,如下圖所示:
程式範例Ch3-4-2.c • 程式目標 • 在C程式中宣告二維字元陣列和字串的指標陣列儲存數個字串, 然後使用巢狀迴圈顯示各字串!! • 執行結果如下 • 顯示二維字元陣列的內容 • names[0]=[ 陳會安 ] • names[1]=[ 江小魚 ] • names[2]=[ 張無忌 ] • names[3]=[ 楊過 ] • 顯示指標陣列的內容 • name[0]=[ 陳會安] • name[1]=[ 江小魚] • name[2]=[ 張無忌] • name[3]=[ 楊過]
/* 顯示指標陣列的元素值 */ printf("--------------------\n"); printf("顯示指標陣列的內容: \n"); for ( i = 0; i < ROWS; i++ ) { printf("name[%d] =[", i); ptr = name[i]; /* 取得每一個指標 */ for ( j = 0; *(ptr+j) != '\0'; j++) printf("%c", *(ptr+j)); printf("]\n"); } system("PAUSE"); return 0; } /* 程式範例: Ch3-4-2.c */ #include <stdio.h> #include <stdlib.h> #define ROWS 4 #define COLUMNS 10 /* 主程式 */ int main() { /* 宣告變數 */ int i, j; /* 建立二維的字元陣列且指定初值 */ char names[ROWS][COLUMNS] = { "陳會安", "江小魚", "張無忌", "楊過" }; /* 指標陣列 */ char *name[ROWS] = { "陳會安", "江小魚", "張無忌", "楊過" }; char *ptr; /* 顯示二維陣列的元素值 */ printf("顯示二維字元陣列的內容: \n"); for ( i = 0; i < ROWS; i++ ) { printf("names[%d]=[", i); ptr = names[i]; for ( j = 0; j < COLUMNS; j++) printf("%c", *(ptr+j)); printf("]\n"); }
3-5 指標的應用 - 字串處理(說明) • C語言的字串並不是一種基本資料型態,而是一種字元陣列 • 所以並沒有支援指定、連結和比較等運算子 • 取而代之的是在標準函式庫<string.h>提供多種字串處理函數。 • 接下來, 將討論一些基本字串函數的實作,著重於說明如何設計這些字串函數所需的程式技巧,這些技巧都屬於指標的應用。
3-5 指標的應用 - 字串處理(標頭檔) 01: /* 程式範例: Ch3-5.h */ 02: char str[81]; /* 儲存字串的字元陣列 */ 03: /* 抽象資料型態的操作函數宣告 */ 04: extern int strLen(char *str); 05: extern char *strCpy(char *dest, char *source); 06: extern char *strCat(char *dest, char *source); 07: extern int strCmp(char *source, char *target); 08: extern char *strPos(char *source, char *target);
3-5 指標的應用 - 字串處理(1) 函數strLen():取得字串長度 • 字串是一個字元陣列 • 計算字串長度的方法就是一維陣列的走訪和指標的遞增運算,如下所示: char *ptr = str; while ( *ptr != '\0' ) ptr++; return ptr - str; • 上述程式碼使用while迴圈走訪到字串的最後1個字元,然後使用ptr-str指標減法運算取得字串長度。