本篇討論數組與指針之間的關系,分別以一維數組與二維數組為例進行說明。
一. 一維數組。
首先,讓我們明確以下兩點:
第一,數組名是一個固定的東西,它只能代表一個數組,也就是說,不允許這個數組名在后面又去表示另一個數組。
第二,數組名是一個指針,並且是一個常量指針。
現有一條語句: array[2] { 0, 1 },這條語句定義了一個數組,數組名是 array,該數組包含 2 個元素,其值分別為 0 和 1。我們訪問數組元素通過[ ] 運算符,比如 array[ 0 ] ,它的值就是 0 。這個 array ,也就是數組名,實質上是一個指針,它指向這個數組第一個元素的首地址。通過下面代碼,我們可以清楚的知道,array 的地址和 array[ 0 ]的地址是一樣的。
1 #include <iostream> 2 using namespace std; 3 4 int main() { 5 int a[2] { 0, 1 }; 6 cout << "a address : " << &a << endl; 7 cout << "a[0] address : " << &a[0] << endl; 8 9 return 0; 10 }
二. 二維數組
二維數組,我們可以把它看作一個矩陣,由行列組成。現有一條語句 array_2d [ 2 ] [ 3 ] ,這條語句聲明了一個二維數組,它有兩行三列共 6 個元素。我們換一個角度,不要把它看成一個整體,而是看成 2 個 一維數組的組合,每個一維數組有 3 個元素。這兩個一維數組的首元素分別是 array_2d [ 0 ] [ 0 ] , array_2d [ 1 ] [ 0 ]。為了更清楚的查看結果,請看以下代碼:
1 #include <iostream> 2 using namespace std; 3 4 int main() { 5 int a[2] { 0, 1 }; 6 int b[2][3] { { 1, 2, 3 }, { 3, 2, 1 } }; 7 cout << "a address : " << &a << endl; 8 cout << "a[0] address : " << &a[0] << endl; 9 10 cout << "b address : " << &b << endl; 11 cout << "b[0] address : " << &b[0] << endl; 12 cout << "b[1] address : " << &b[1] << endl; 13 cout << "b[0][0] address : " << &b[0][0] << endl; 14 cout << "b[1][0] address : " << &b[1][0] << endl; 15 16 return 0; 17 }
這段代碼的運行結果如下:
1. a 和 a[ 0 ] 的地址一樣。
2. b 與 b[ 0 ] 以及 b[ 0 ][ 0 ]這三者的地址一樣。
這表明數組名是數組首元素的地址,不論數組是一維還是二維,都遵循這個規律。
三. 當動態分配內存時,數組名與首元素地址是什么關系?
很多時候,之間定義固定長度的數組,並不能達到我們想要的效果,因此需要動態分配內存。請看如下代碼:
1 #include <iostream> 2 using namespace std; 3 4 int main() { 5 int* new_array = new int[5]; 6 int** new_array_2d = new int* [5]; 7 for (int i = 0; i < 5; ++i) { 8 new_array_2d[i] = new int[5]; 9 } 10 11 cout << "New_array address : " << &new_array << endl; 12 cout << "New_array[0] address : " << &new_array[0] << endl; 13 cout << "New_array_2d address : " << &new_array_2d << endl; 14 cout << "New_array_2d[0] address : " << &new_array_2d[0] << endl; 15 cout << "New_array_2d[0][0] address : " << &new_array_2d[0][0] << endl; 16 17 return 0; 18 }
在第 5 行,我們動態分配了一個元素個數為 5 的數組, new 運算符返回了首地址,並賦值給指針 new_array,現在 new_array 可以理解為該數組的名字。數組元素也可以通過 [ ] 運算符來訪問。
但是代碼的運行結果如下:
1. new_array 的地址不是 new_array [ 0 ] 的地址。
2. new_array_2d 的地址, new_array_2d [ 0 ] 的地址, new_array_2d [ 0 ] [ 0 ] 的地址,互不相同。
通過上面的結果我們可以清楚的知道,在動態分配內存時,數組名(即指針)的地址,與數組元素的地址無關。
代碼中有幾處需要解釋:
第一,new 運算符返回的是一個地址,因此需要用一個指針變量保存。
第二,new運算符分配連續空間時,指針同樣保存的是首地址。
第三,在二維數組動態分配的時候,用到了二級指針,因此你會看到 int** x這種寫法。指針變量里存儲的是地址,當這個指針指向的數據是 int 型,就寫成 int* x,而當這個指針指向另外一個指針時,自然就應該寫成 int** x(可以將 int 和第一個星號看成一個整體,表示指針數據類型,將第二個星號和變量名看成普通的變量聲明即可)。前面說過,可以將二維數組看成行列的組合,那么在動態分配二維數組時,先將所有行的地址,當作一個豎着的數組,這個豎着的數組的首地址,賦值給二維數組的名字(即第 6 行定義的二級指針 x ),然后再循環地為每個行(可以看成橫着的數組)分配空間。這個豎着的數組保存了所有首地址,其中的每個元素分別指向每一個橫着的數組,每個橫着的數組保存的真正的元素值。
第四,由三可知,第 6 行代碼右邊就是豎着的數組,其中的每個元素都是指針,因此這個數組是指針數組,應該用 new int* [ ]。而第 7 行到第 9 行里循環分配的空間是橫着的數組,里面每個元素是一個整數,因此寫為 new int [ ] 。
