在宣告陣列之後,使用到陣列變數時,會取得首元素的位址,例如在下面的程式中將指出,陣列 arr
與 &arr[0]
的值是相同的:
#include <iostream>
using namespace std;
int main() {
int arr[10] = {0};
cout << "arr:\t\t" << arr << endl
<< "&arr[0]:\t" << &arr[0] << endl;
return 0;
}
執行結果:
arr: 0x61fe98
&arr[0]: 0x61fe98
之前也曾經談過,陣列索引其實是相對於首元素位址的位移量,下面這個程式以指標運算與陣列索引操作,顯示出相同的對應位址值:
#include <iostream>
using namespace std;
int main() {
constexpr int LENGTH = 10;
int arr[LENGTH] = {0};
int *p = arr;
for(int i = 0; i < LENGTH; i++) {
cout << "&arr[" << i << "]: " << &arr[i]
<< "\tp+" << i << ": " << p + i << endl;
}
return 0;
}
每個元素的位址型態是 int*
,執行結果如下:
&arr[0]: 0x61fe8c p+0: 0x61fe8c
&arr[1]: 0x61fe90 p+1: 0x61fe90
&arr[2]: 0x61fe94 p+2: 0x61fe94
&arr[3]: 0x61fe98 p+3: 0x61fe98
&arr[4]: 0x61fe9c p+4: 0x61fe9c
&arr[5]: 0x61fea0 p+5: 0x61fea0
&arr[6]: 0x61fea4 p+6: 0x61fea4
&arr[7]: 0x61fea8 p+7: 0x61fea8
&arr[8]: 0x61feac p+8: 0x61feac
&arr[9]: 0x61feb0 p+9: 0x61feb0
在這個程式中,將陣列的首元素位址指定給 p
,然後對 p
遞增運算,每遞增一個單位,陣列相對應索引的元素之位址都相同。
也可以利用指標運算來取出陣列的元素值,如以下的程式所示:
#include <iostream>
using namespace std;
int main() {
constexpr int LENGTH = 5;
int arr[LENGTH] = {10, 20, 30, 40, 50};
int *p = arr;
// 以指標方式存取
for(int i = 0; i < LENGTH; i++) {
cout << "*(p + " << i << "): " << *(p + i) << endl;
}
cout << endl;
// 以指標方式存取資料
for(int i = 0; i < LENGTH; i++) {
cout << "*(arr + " << i << "): " << *(arr+i) << endl;
}
cout << endl;
return 0;
}
執行結果:
*(p + 0): 10
*(p + 1): 20
*(p + 2): 30
*(p + 3): 40
*(p + 4): 50
*(arr + 0): 10
*(arr + 1): 20
*(arr + 2): 30
*(arr + 3): 40
*(arr + 4): 50
在〈陣列〉中談過,可以使用 sizeof
來計算陣列長度,在認識指標及其運算後,可以知道透過 C++ 11 提供的 begin
與 end
函式,也可以計算陣列長度:
#include <iostream>
using namespace std;
int main() {
constexpr int LENGTH = 5;
int arr[LENGTH] = {10, 20, 30, 40, 50};
cout << sizeof(arr)/sizeof(*arr) << endl; // 顯示 5
cout << end(arr) - begin(arr) << endl; // 顯示 5
return 0;
}
實際上,透過以下也可以計算出陣列長度:
constexpr int LENGTH = 5;
int arr[LENGTH] = {10, 20, 30, 40, 50};
int len = *(&arr + 1) - arr;
來解釋一下為什麼這行得通,如果使用 &arr
會取得 arr
變數的位址值,也就是陣列資料儲存的位址,與首元素位址是相同的值:
#include <iostream>
using namespace std;
int main() {
constexpr int LENGTH = 5;
int arr[LENGTH] = {10, 20, 30, 40, 50};
cout << arr << endl; // 顯示 0x61fea8
cout << &arr << endl; // 顯示 0x61fea8
return 0;
}
每個陣列元素的位址型態是 int*
,這表示對它進行運算時,是以 int
長度為單位,而 arr
變數的位址處就是陣列資料的開端,&arr
型態會是…呃…int (*)[LENGTH]
,如果想宣告相對應的變數,可以如下:
int (*p)[LENGTH] = &arr;
int (*)[LENGTH]
表示,對它進行運算時,是以 LENGTH
個 int
長度為單位,因此 &arr + 1
的結果,會是陣列使用的空間後之位址,而 *(&arr + 1)
的值型態會回到 int*
,也就是最後一個元素後之位址,這時就可以與 int*
的 arr
進行相減,也就是與第一個元素之位址相減,就可以得到陣列長度了。
舉這個例子的重點之一是,對於同一個位址,指標的型態決定了該怎麼看得相對應相加、相減計算;另一個重點是,透過陣列變數會取得首元素的位址,將陣列變數指定給指標 p
,就只是取得首元素位址並儲存在 p
,如果將 p
傳給 sizeof
,那使用的會是指標 p
的型態,而不是原陣列的型態,這會令 sizeof
、以及方才那神奇計算長度的方式失效,例如:
#include <iostream>
using namespace std;
int main() {
constexpr int LENGTH = 5;
int arr[LENGTH] = {10, 20, 30, 40, 50};
int *p = arr;
cout << p << endl; // 顯示 0x61fea8
cout << &p << endl; // 顯示 0x61fea4
cout << sizeof(p)/sizeof(*p) << endl; // 顯示 1
cout << *(&p + 1) - p << endl; // 顯示 -1605544
return 0;
}
以上的程式若試圖使用 begin(p)
或 end(p)
,會編譯失敗,因此試著對 p
進行 for range 語法,也會導致編譯失敗。