指標和陣列有密切的關係。我們通常會習慣使用索引,也就是 index 去存取陣列的元素。而也正是因為陣列在記憶體內的排列特性,讓陣列元素也非常適合用指標來存取。
陣列
首先要先介紹指標加減法。當我們操作加減法時,是針對指標所指向的資料型態大小作處理。例如我宣告了一個指標變數 int *ptr
,因為指向整數,因此 ptr+1
是將位址的值加上四個 bytes。
因為記憶體會以連續的記憶體空間配置給陣列,所以我們可以利用上面指標的算術特性操作。另外有一句話很重要:陣列的名稱本身是存放一個位置的指標常數。
例如我們宣告了一個陣列名稱叫做 arr
,它本身是指標常數,指向陣列的位置,但無法變更名稱。這個指標常數的位置等於指標常數的值。另外,陣列第一個元素的位址就代表了陣列的位置。
好像繞口令,可以直接透過下面的實作看得更清楚:
#include <stdio.h> #include <stdlib.h> int main (void){ int a[5] = {1, 100, 321, 6, 34}; printf("a = %p\n", a); printf("&a = %p\n", &a); for (int i = 0; i < 5; i++){ printf("a[%d] = %d\n", i, a[i]); } for (int i = 0; i < 5; i++){ printf("&a[%d] = %p\n", i, &a[i]); } }
output
a = 0x7fff93300ff0
&a = 0x7fff93300ff0
a[0] = 1
a[1] = 100
a[2] = 321
a[3] = 6
a[4] = 34
&a[0] = 0x7fff93300ff0
&a[1] = 0x7fff93300ff4
&a[2] = 0x7fff93300ff8
&a[3] = 0x7fff93300ffc
&a[4] = 0x7fff93301000
指標加減法操作
熟悉上面特性之後,就可以實際利用指標的加減法演練一次陣列操作:
#include <stdio.h> #include <stdlib.h> int main (void){ int a[5] = {9, 8, 7, 6, 5}; for (int i = 0; i < 5; i++){ printf("a[%d] = %d\n", i, *(a+i)); } }
其實只是把第一個程式碼的 a[i]
改成 *(a+i)
而已。
也就是說 a+i
就代表了 a[i]
的位址,理所當然 *(a+i)
就可以取出 a[i]
的值。
然而我們有一點要注意,也是上面強調過的:陣列名稱本身是指標常數,它指向陣列的位置,但無法變更名稱。所以,a = a + 1
這種操作會失敗,就跟你說了不能改變名稱吼。
但若是宣告一個指標變數來指向 a
,就可以避開這個問題:int *ptr = a
,代表我宣告了指標 ptr
指向 a
。ptr = ptr + 1
,代表指標 ptr
指向 a
的下一個元素位址。
二維陣列和指標
基本上上述都是一維陣列的指標操作方法。二維陣列也很常見到,要怎麼操作呢?
首先要了解二維陣列的儲存方式:
和一維陣列的概念很像,只是我們可以把二維看作是一維陣列的堆疊。
很重要的一句話:陣列名稱本身是指標常數。有了上述概念後,可能對於如何指向二維陣列內的元素有一點想法了,或許我們會需要兩個指標去指這個元素。因此,接下來要稍微跳開,講一下雙重指標。
指標除了指向任何一種資料型態的變數,還可以指向指標!宣告方法也很簡單,就是多加一個 *
:
資料型態 **雙重指標;
int **ptr;
int *(*ptr);
兩種寫法都可以。概念上可以看下圖:
我們一樣用實際操作來理解:
#include <stdio.h> #include <stdlib.h> int main (void){ int n=999; int *p, **pp; p = &n; pp = &p; printf("n = %d; &n = %p \n", n, &n); printf("*p = %d; p = %p; &p = %p \n", *p, p, &p); printf("**pp = %d; *pp = %p; pp = %p; &pp = %p \n", **pp, *pp, pp, &pp); }
output
n = 999; &n = 0x7ffceb269c14
*p = 999; p = 0x7ffceb269c14; &p = 0x7ffceb269c18
**pp = 999; *pp = 0x7ffceb269c14; pp = 0x7ffceb269c18; &pp = 0x7ffceb269c20
OK,這樣子就越來越清楚二維陣列的可能用法了。
從之前一維陣列我們知道 a+i
就代表了 a[i]
的位址,*(a+i)
就可以取出 a[i]
的值,而二維陣列還要再往更裡面取值,所以就是 *(*(a+i)+j)
這種形式,也代表著取出二維陣列第 i
列第 j
行(index 從 0 開始):
直接看一下實作:
#include <stdio.h> #include <stdlib.h> int main (void){ int arr[4][3] = {{11, 55, 88}, {46, 89, 23}, {17, 30, 14}, {99, 27, 31}}; printf("列印各個元素:\n"); for (int i = 0; i < 4; i++){ for (int j = 0; j < 3; j++){ printf("%3d", *(*(arr+i)+j)); } printf("\n"); } printf("\n各個元素位址:\n"); for (int i = 0; i < 4; i++){ for (int j = 0; j < 3; j++){ printf("arr[%d][%d]=%d; (%p)\n", i, j, *(*(arr+i)+j), *(arr+i)+j); } } }
output
列印各個元素:
11 55 88
46 89 23
17 30 14
99 27 31
各個元素位址:
arr[0][0]=11; (0x7ffff5f5a940)
arr[0][1]=55; (0x7ffff5f5a944)
arr[0][2]=88; (0x7ffff5f5a948)
arr[1][0]=46; (0x7ffff5f5a94c)
arr[1][1]=89; (0x7ffff5f5a950)
arr[1][2]=23; (0x7ffff5f5a954)
arr[2][0]=17; (0x7ffff5f5a958)
arr[2][1]=30; (0x7ffff5f5a95c)
arr[2][2]=14; (0x7ffff5f5a960)
arr[3][0]=99; (0x7ffff5f5a964)
arr[3][1]=27; (0x7ffff5f5a968)
arr[3][2]=31; (0x7ffff5f5a96c)
關於指標和陣列的簡單介紹就先到此結束,以後若有更深的理解還會再更新!