13
3-1 指標與一維陣列 3-2 指標與二維陣列 3-3 陣列指標 3-4 為什麼 parr 等同於 *parr 3-5 指向陣列的指標 3-6 多重指標 3-7 命令列引數 3-8 除錯題 3-9 問題演練 3-10 程式實作 3 3 指標與陣列

指標與陣列 - 碁峰資訊epaper.gotop.com.tw/pdf/ACL028800.pdf · 指標變數與指標常數的不同處是,前者可以使用遞增 運算子(++),或遞減運算子(--)來遞增和遞減指標,但後者不行。因此,

  • Upload
    others

  • View
    16

  • Download
    0

Embed Size (px)

Citation preview

  • 3-1 指標與一維陣列

    3-2 指標與二維陣列

    3-3 陣列指標

    3-4 為什麼 parr 等同於*parr?

    3-5 指向陣列的指標

    3-6 多重指標

    3-7 命令列引數

    3-8 除錯題

    3-9 問題演練

    3-10 程式實作

    33 指標與陣列

  • Part 1 C程式語言篇 32

    指標其實就是一位址。陣列的名稱,表示此陣列第一個元素的位址,所

    以它也是指標。由此可知,指標與陣列的關係是很密切的。為了與指標

    變數 (pointer variable) 有所區別,我們稱陣列名稱為是一指標常數

    (pointer constant)。指標變數與指標常數的不同處是,前者可以使用遞增

    運算子 (++),或遞減運算子 (--)來遞增和遞減指標,但後者不行。因此,

    若在程式中使用 arr++ 或 arr--,則會出現錯誤的訊息。

    3-1 指標與一維陣列 我 們 先 來 看 指 標 與 一 維 陣 列 (one dimension) 的 關 係 。 請 參 閱 範 例

    pointerArr1-5。

    範例 pointerArr1-5

    /* pointerArr1-5.c */ #include #include int main() { int arr[]= {100, 101, 102}; int *ptr = arr; int i, size = 0; size = (sizeof arr/ sizeof (arr[0]) ); /* ------------Using arr--------------*/ printf("使用 arr 指標常數來表示:\n"); for(i = 0; i < size; i++) printf("&arr[%d] = %x\n", i, &arr[i]); printf("\n"); for(i = 0; i < size; i++) printf("arr+%d = %x\n", i, arr+i); printf("\n"); for(i = 0; i < size; i++) printf("arr[%d] = %x\n", i, arr[i]); printf("\n"); for(i = 0; i < size; i++) printf("*(arr+%d) = %x\n", i, *(arr+i)); /*--------------Using ptr-------------*/ printf("\n使用 ptr 指標變數來表示:\n"); for(i = 0; i < size; i++)

  • Chapter 3 指標與陣列 33

    printf("ptr+%d = %x\n", i, ptr+i); printf("\n"); for(i = 0; i < size; i++) printf("ptr[%d] = %d\n", i, ptr[i]); printf("\n"); for(i = 0; i < size; i++) printf("*(ptr+%d) = %d\n", i, *(ptr+i)); getch(); return 0; }

    輸出結果

    從輸出結果得知,arr 是陣列名稱,它是指標常數,而 ptr 是指標變數。

    arr 表示此陣列第一個元素的位址,亦即 arr 等同於 &arr[0]。

  • Part 1 C程式語言篇 34

    arr 可以使用指標變數的 * 表示符號,如 *arr 等同於 arr[0],*(arr+1) 等

    同於 arr[1],依此類推。同理,ptr 也可以使用指標常數的 []表示符號,如

    目前 ptr 所指向變數位址的值為 ptr[0],其等同於*ptr,而 ptr[1] 等同於

    *(ptr+1),依此類推。

    再來看範例 pionterArr1-10。

    範例 pointerArr1-10 /* pointerArr1-10.c */ #include #include int main() { int i[] = {100, 200, 300, 400, 500}; int *ptr = i+2; int k; printf("ptr[-2]=%d\n", ptr[-2]); printf("ptr[-1]=%d\n", ptr[-1]); printf("ptr[0]=%d\n", ptr[0]); printf("ptr[1]=%d\n", ptr[1]); printf("ptr[2]=%d\n\n", ptr[2]); ptr++; printf("After executing ptr++....\n"); printf("ptr[0]=%d\n", ptr[0]); printf("*(ptr+0)=%d\n", *(ptr+0)); printf("ptr[1]=%d\n", ptr[1]); printf("*(ptr+1)=%d\n", *(ptr+1)); getch(); return 0; }

    輸出結果

  • Chapter 3 指標與陣列 35

    ptr 與 i 陣列的關係圖,如下所示:

    ptr:i+2

    i i+1 i+2 i+3 i+4

    i[0]:100 i[1]:200 i[2]:300 i[3]:400 i[4]:500

    程式一開始將 ptr 指向 i+2(它是 i[2] 的位址),所以 ptr[0] 等於 i[2],因

    為 ptr[0]表示目前 ptr 所指向變數位址的值。同時也得知 ptr[-1] 是 i[1]、

    ptr[-2] 是 i[0]、ptr[1]是 i[3]、ptr[2] 是 i[4]。我們從此範例得到以下的公

    式 ptr[i] == *(ptr+i)

    大部份的使用者都是以*(ptr+i),間接存取陣列中索引為 i 的元素值。

    當指標與++遞增運算子一起運算時,必須注意++的作用點在那裏,是對

    位址加 1,或是將變數值加 1。若是對位址加 1,則將指標移到下一元素

    的位址。請參閱範例 pointer&++。

    範例 pointer&++

    /* pointer&++.c */ #include #include int main() { int i[] = {100, 200, 300, 400, 500}; int *pi = i; printf("i=%p, pi=%p\n", i, pi); printf("i[0]=%d\n", i[0]); printf("*pi=%d\n\n", *pi); pi+1; printf("After pi+1, pi=%p\n", pi);; printf("*pi=%d\n\n", *pi); pi++; printf("After pi++, pi=%p\n", pi); printf("*pi =%d\n", *pi); getch(); return 0; }

  • Part 1 C程式語言篇 36

    輸出結果

    i=0022FF50, pi=0022FF50 i[0]=100 *pi=100 After pi+1, pi=0022FF50 *pi=100 After pi++, pi=0022FF54 *pi=200

    從輸出結果得知,pi+1; 只是將目前的 pi 往下移到下一個元素的位址,

    它並沒有覆蓋 pi,而 pi++; 不僅將目前的 pi 移到下一個元素的位址,而

    且還將此新值覆蓋 pi。我們可以對指標變數 pi 做++的動作,但不可以對

    陣列名稱 i 做++的動作。

    當指標、遞增 (++)運算子或遞減 (--)運算子,及 * 這三個運算子在同一敘

    述時,要注意++的作用點在那裏。請參閱範例 pointer&++2。

    範例 pointer&++2

    /* pointer&++2.c */ #include #include int main() { int i[] = {100, 200, 300, 400, 500}; int *pi = i; printf("...%d\n", *pi++); printf("*pi = %d\n", *pi); printf("...%d\n", *++pi); printf("*pi = %d\n", *pi); printf("...%d\n", ++*pi); printf("*pi = %d\n", *pi); getch(); return 0; }

    輸出結果

    …100 *pi = 200 …300 *pi=300 …301 *pi = 301

  • Chapter 3 指標與陣列 37

    從程式定義中

    int i[ ] = {100, 200, 300, 400, 500}; int *pi = i;

    得知其示意圖如下:

    pi

    i i+1 i+2 i+3 i+4

    100 200 300 400 500

    下一敘述

    *pi++;

    當*和++在同一敘述時,要注意++的作用是,對位址加 1 或對值加 1。若

    對位址加 1,表示將 pi 指標移到下一位址。由於*和++的運算優先順序相

    同,且其結合性是由右至左,因此*pi++其實就是*(pi++);,但此處的++

    為後繼加,所以先得到*pi 為 100 之後,才會處理++的動作。以此敘述先

    印出 100,再將 pi 指向下一位址。如下圖所示:

    pi i i+1 i+2 i+3 i+4

    100 200 300 400 500

    接下來的

    *++pi;

    由於是此敘述相當於*(++pi),此處的 ++ 是前置加,所以 pi 指標先移到

    了下一位址,再印出*pi 的值 (300),如下圖所示:

    pi i i+1 i+2 i+3 i+4

    100 200 300 400 500

    最後

    ++*pi;

    此敘述相當於++(*pi),由此可知,++乃針對*pi 的值加 1,此敘述等同於

  • Part 1 C程式語言篇 38

    *pi = *pi + 1;

    將*pi(= 300)加 1,再放入*pi 中,如下圖所示:

    pi i i+1 i+2 i+3 i+4

    100 200 301 400 500

    最後,*pi 的運算結果為 301。

    3-2 指標與二維陣列 一維陣列與指標的關係,由上述可得知,一維陣列的元素值,可利用 []

    或一個*得到,如有一陣列如下:

    int i[7] = {0, 1, 2, 3, 4, 5, 6}; int *ptr=i;

    則*(i+2) 或 i[2] 或*(ptr+2) 或 ptr[2],皆表示陣列某一元素的值。而二維

    陣列 (two dimension),則需兩個*,或一個*一個 [ ],或兩個 [ ],才可得到

    陣列的元素值,其餘的表示法,只能得到陣列元素的位址。

    假設有一個二維陣列的定義如下:

    int j[2][3] = {0, 1, 2, 3, 4, 5};

    j 0 1 2

    j+1 3 4 5

    其中 j 是此陣列的名稱,表示此陣列第一列第一行元素之位址,而 j+1 是

    第二列第一行元素的位址,除此之外

    j[0] 0 1 2

    j[1] 3 4 5

    j[0]和 j[1]的意思等同於 j 和 j+1,表示第一列第一個元素和第二列第一個

    元素的位址。 j 和 j[0] 雖然表示同一元素的位址,但兩者加 1 個單位的意

    思是不相同的。

  • Chapter 3 指標與陣列 39

    j+1 表示第二列第一個元素的位址,即 j+1==&j[1][0]; 而 j[0]+1 是第一列

    第二個元素的位址,即 j[0]+1==&j[0][1],如下圖所示。

    j[0]+1

    j[0] 0 1 2

    j[1] 3 4 5

    有關二維陣列的每一元素所在記憶體的觀念,已在第一章討論過,請參

    閱範例 address2Array-5 和 address2Array-10。

    接下來,試問*j 和*(j+1)是某一元素的位址,還是某一元素的值呢?答案

    是某一元素的位址。j 與*j 皆表示第一列第一行元素的位址 (&j[0][0])。而

    j+1 與*(j+1) 皆表示第二列第一行元素的位址 (&j[1][0]),如下圖所示:

    *j, j 0 1 2

    *(j+1), j+1 3 4 5

    雖然 j 與*j 表示相同的意思,但兩者各加 1,表示的意義是不相同的。j+1

    表示第二列第一行的位址,而 *j+1 表示第一列第二行的位址 (&j[0][1])。

    同 理 , *(j+1)+1 是 第 二 列 第 二 行 的 位 址 (&j[1][1]) 。 請 參 閱 範 例

    pointerArr2-5。

    範例 pointerArr2-5

    /* pointerArr2-5 */ #include #include int main() { int j[2][3] = {10, 20, 30, 40, 50, 60}; int k; for(k=0; k

  • Part 1 C程式語言篇 40

    輸出結果

    j[0] = 0022FF50 j[1] = 0022FF5c j[2] = 0022FF68 j+0 = 0022FF50 j+1 = 0022FF5c j+2 = 0022FF68 *(j+0) = 0022FF50 *(j+1) = 0022FF5c *(j+2) = 0022FF68

    綜合上述,若有一個二維陣列 k,如下所示:

    int k[3][4] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

    陣列的示意圖,如下圖所示:

    0 1 2 3

    4 5 6 7

    8 9 10 11

    如何得到元素值 6(位於第二列第三個元素)的位址呢?答案如下:

    &k[1][2],k[1]+2,*(k+1)+2

    其中&k[1][2]很清楚的可以看出,它表示 k[1][2]元素的位址。*(k+1)和 k[1]

    皆表示第二列第一個元素的位址,所以再加 2,皆可得到第二列第三個元

    素的位址。

    當我們得到元素的位址後,再加上一個*就可以得到該元素的值,所以

    k[1][2],*(k[1]+2),*(*(k+1)+2)

    都可以得到陣列第二列第三行的值。由以上的敘述,可以導出下一公式:

    k[x][y] == *(k[x]+y) == *(*(k+x)+y)

    這一公式是可以很容易理解的,因為 *和 []是互通的。請參閱範例

    pointerArr2-10。

  • Chapter 3 指標與陣列 41

    範例 pointerArr2-10

    /* pointerArr2-10.c */ #include #include int main() { int k[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; printf("&k[1][2] = %p\n", &k[1][2]); printf("k[1][2] = %d\n", k[1][2]); printf("*(k + 1) + 2 = %p\n", *(k + 1) + 2); printf("*(*(k + 1) + 2) = %d\n", *(*(k + 1) + 2)); printf("k[1] + 2 = %p\n", k[1] + 2); printf("*(k[1] + 2) = %d\n", *(k[1] + 2)); getch(); return 0; }

    輸出結果

    範例程式使用三種方式,得到 k[1][2] 的位址與數值。要得到 k[1][2] 的值

    7,可以使用*(k[1]+2)及*(*(k + 1) + 2)。若要得到 k[1][2] 的位址,則可

    使用&k[1][2]、k[1] + 2 及*(k + 1) + 2,此處以 %p 印出陣列第二列第三個

    元素的位址是 0022FF58。

    3-3 陣列指標 陣列指標(pointers of array)表示陣列的元素,皆指向某一資料型態的

    指標,如

    char *parr[4] = {“Department”, “of”, “Information”, “Management”};

  • Part 1 C程式語言篇 42

    由於 [ ]運算子運算優先順序高於 * 運算子,因此,parr 是 4 個元素組成

    的陣列,陣列中的每一元素皆是指向 char 資料型態的指標。若將上述的

    定義以圖形加以輔助的話,則可以很快的得到答案。

    此敘述的陣列名稱 parr,表示第一個元素 parr[0] 的位址,而且是一指標

    常數,所以*parr 是 parr[0],而 parr[0]是“Department”字串中 D 字元的位

    址。

    parr 可視為二維陣列(因為指標 (*)和陣列 ([])是互通),我們可利用下列

    三種方式:

    1. 兩個*,如**parr。

    2. 一個*,搭配一個 [ ],如*parr[0]。

    3. 兩個 [ ],如 parr[0][0]。

    印出 Department 字串中的 D 字元。除了上述三種方式外,其餘的表示法

    將得到位址而已。請參閱範例 pointerOfArray-5。

  • Chapter 3 指標與陣列 43

    範例 pointerOfArray-5

    /* pointerOfArray-5.c */ #include #include int main() { char *parr[] = {"Department", "of", "Information", "Management"}; printf("*parr[3] = %c\n", *parr[3]); printf("**(parr+3) = %c\n", **(parr+3)); printf("parr[3][0] = %c\n", parr[3][0]); printf("parr[2] = %s\n", parr[2]); printf("*(parr+2) = %s\n", *(parr+2)); system(“PAUSE”); return 0; }

    輸出結果

    *parr[3] = M **(parr+3) = M parr[3][0] = M parr[2] = Information *(parr+2) = Information

    從程式得知,要印出某一字串的字元,則可利用 *parr[3]、 **(parr+3)、

    parr[3][0] 這三種方式印出,並以%c 為其格式(因為 parr 的每一元素皆為

    指向 char 的指標),而列印字串只需知道字串的第一個字元的位址即可,

    並以%s 格式輸出,如 parr[2]和*(parr+2)皆為指向某一字元的位址。

    承上例,若欲列印某字串中的子字串,如“Department”字串中的“ment”,

    則可利用下列敘述之一加以輸出。

    1. printf(“%s”, parr[0]+6);

    2. printf(“%s”, *parr+6);