陣列 (C++)
C++ Arrays
陣列 (array) 是用來儲存相同資料型態元素的集合,陣列中所有元素會使用記憶體中連續的空間來儲存,且陣列必須明確定義其大小,執行期間不能更改。
一維陣列 (One Dimensional Arrays)
一維陣列可視為一組線性系列元素。假設有一組整數資料:10 、20、30 、40 以及 50 存入一名為 A 的陣列,其對應的索引值、位址以及元素名稱如圖一所示。在此,每一整數佔記憶體空間為 4 個位元組 (Bytes),且 C/C++ 語言中陣列的起始索引值為 0。此ㄧ維陣列元素 A[0] 的位址,被稱為基底位址 (base address)。
圖一 儲存有10 、20、30 以及 40 四個整數的一維陣列
圖二 陣列 A 與其元素的位址,一個整數資料型態的元素佔記憶體空間 4 位組
A[i] 的位址 = A[0] 的位址 + i $\times $ sizeof(int)
其中的 i 是陣列的索引值須為正整數,其值起始於 0 至 sizeof (A) / sizeof(int) - 1。利用 sizeof ( ) 運算子可找出陣列或各種資料型態,佔記憶體多少個位元組。例如 sizeof (A) 即可用來找出整個陣列 A 佔記憶體多少個位元組,sizeof(int) 則用來找出一個整數資料型態數值佔記憶體多少個位元組。因此,sizeof (A) / sizeof(int) 或 sizeof (A) / sizeof(A[0]) 就可找出陣列 A 的元素個數為何。事實上,陣列名稱 A,就是一個指標 (pointer),也就是常數指標 A 本身儲存著陣列在記憶體中的起始位址 ( 等同於 &A[0] ),而索引值 i $\times $ sizeof(int) 的值就是元素位置在記憶體中的偏移量 (offset) 。因此,對陣列名稱進行操作,其實就是在操作這一個指標。
陣列第 i 個元素,可直接以 A[i] 對其進行存取;若採用指標方式,則以 *(A+i) 對其進行存取,系統會自動將 i 乘上 sizeof(int) 再加上 A 存放陣列起始位址,找到要存取元素的位址。
ㄧ維陣列的宣告 (Declaration) 與初始化 (Initialization)
陣列的宣告必須包含有資料型態、陣列名稱與元素的個數。而一維陣列的宣告方式如下:
資料型態 陣列名稱[元素個數];
例如:
int number[5];
宣告了名為 number 的陣列,可存放 5 個資料型態為 int 的整數,接下來就可以輸入或指定各個元素值了。
例如:
const int maxSize = 5;
int number[maxSize];
for (int i = 0; i < maxSize; i++)
{
cout << "輸入第" << i << "個元素值:";
cin >> number[i];
}
cout << "以 number[2] 方式取得陣列第 2 個元素為 " << number[2] << " << "\n";
cout << "以 *(number + 2) 方式取得陣列第 2 個元素為 " << *(number + 2) << " << "\n";
在上例中我們以宣告資料型態為 const int 的常數整數來存放指定的陣列大小。須注意的是:由於陣列的大小在宣告時就必須固定下來,因此用來存放陣列的大小的資料型態必須是 const int ,而不能是 int,否則編譯時會發生錯誤。此外,上例嘗試以 number[2] 方式以及 *(number + 2) 方式來取得陣列第 2 個元素值,下面範例程式一將實際驗證二者的確都可達到相同目的。
範例程式一
#include <iostream>
using namespace std;
const int maxSize = 5;
int number[maxSize];
int main()
{
int i;
for (i = 0; i < maxSize; i++)
{
cout << "輸入第" << i << "個元素值:";
cin >> number[i];
}
cout << "---------------------\n";
int j;
for (j = 0; j < maxSize; j++)
cout << "number[" << j << "] = "<< number[j] << "\n";
cout << "---------------------\n";
for (j = 0; j < maxSize; j++)
cout << "*(number+" << j << ") = " << *(number + j) << "\n";
return 0;
}
範例程式一輸出
此外,我們也可以在宣告陣列時,一併完成該陣列的初始化。且若陣列宣告時一併初始化,則元素的個數可以直接指明,也可以省略。
例如:
double price[5] = {50.4, 86.2, 14.0, 36.7, 66.9};
宣告了名為 price 的陣列,可存放 5 個資料型態為 double 的雙精度浮點數。元素 price[0] 為 50.4, price[1] 為 86.2,price[2] 為 14.0,price[3] 為 36.7 且 price[4] 為 66.9。在這個例子中,元素的個數在陣列宣告時就指明為 5。
例如:
int score[ ] = {96, 63, 81, 52};
宣告了名為 score 的 int 整數資料型態陣列,score[0] = 96,score[1] = 63,score[2] = 81 且 score[3] = 52。在這個例子中,元素的個數在陣列宣告時並未指明。注意:元素的個數在此省略不指明,編譯器仍可根據初始化的元素個數來得知陣列的大小。
ㄧ維陣列作為函數的形式參數 (Formal Parameters)
當函數的形式參數是傳值參數 (call by value) 時,傳給函數的參數是實際參數 (actual parameter) 的複製值,函數內對參數的修改並不會影響函數外實際參數的值;而當函數的形式參數是參照參數 (reference parameter) 時,函數則是直接操作實際參數 (actual parameter)。然而為了避免大型陣列在呼叫時的大量複製,陣列若要作為函數的形式參數,在 C++ 中只能以參照參數作為函數的形式參數。
例如:
如下的函數
int sum(int x[ ], int arraySize)
{
int sum = 0;
int i;
for (i = 0; i < arraySize; i++)
sum += x[i];
return sum;
}
且假設陣列 number 已宣告如下:
const int maxSize = 5;
int number[maxSize] = {10, 20, 30, 40, 50};
int sumOfArray;
則主程式中要呼叫函數 sum() 可寫成如下的敘述
sumOfArray = sum(number, maxSize);
如前所述,陣列名稱 number 是常數指標,其本身的內容值即為陣列的起始位址。因此,只須將陣列的起始位址以及陣列的大小作為函數 sum 的形式參數,函數 sum 就可以直接對陣列 number[ ] 進行直接的操作。
範例程式二
#include <iostream>
using namespace std;
const int maxSize = 5;
int number[maxSize];
int sum(int x[ ], int arraySize);
int main()
{
for (int i = 0; i < maxSize; i++)
{
cout << "輸入第" << i << "個元素值:";
cin >> number[i];
}
cout << "---------------------\n";
cout << "陣列元素總和為:" << sum(number, maxSize) << "\n";
return 0;
}
int sum(int x[ ], int arraySize)
{
int sum = 0;
int i;
for (i = 0; i < arraySize; i++)
sum += x[i];
return sum;
}
範例程式二輸出
如上所述,陣列在 C++ 中只能以參照參數作為函數的形式參數,當形式參數改變時,實際上就是在直接改變真實參數。倘若在某些情況下,你不希望陣列內的元素值會因參照參數的傳入而受到改變,你可在形式參數宣告時,在其前面加上保留字 const。如果函數內有嘗試修改的敘述,將會導致編譯錯誤。
例如:
宣告如下的函數
int sum(const int x[ ], int arraySize)
就代表陣列 x[ ] 不能在函數 sum( ) 內被修改。
二維陣列 (Two Dimensional Arrays)
如果我們想要用表格或矩陣的方式來存取資料,我們就可以考慮使用二維陣列。二維陣列,可以看作是一維陣列的陣列。不過,即使是二維陣列,實際上其在記憶體中一樣是線性系列配置陣列元素。
圖三 儲存有 10 、20、30 、40、50 以及 60 六個整數的二維陣列 my_2D_array
圖四 二維陣列 my_2D_array 的記憶體配置
二維陣列的宣告與初始化
二維陣列的宣告方式如下:
資料型態 陣列名稱[總列數][總行數];
例如:
int B[4][3];
就是宣告一資料型態為 int ,名稱為 B,4 列 (rows) 3 行 (columns) 的陣列 (注意:在此列是橫向,行是縱向)。
例如:
以下這些敘述可宣告並初始化同樣的二維陣列:
int my_2D_array[2][3] = {{10, 20, 30}, // 第 0 列
{40, 50, 60}}; // 第 1 列
或
int my_2D_array[2][3] = {10, 20, 30, 40, 50, 60};
或
int my_2D_array[ ][3] = {10, 20, 30, 40, 50, 60};
其中可注意到,總列數在宣告一併初始化時是可以省略,但總行數不可省略。因為二維陣列元素在記憶體內是以列的排列方式儲存,依序由第一列儲存,接著第二列,再第三列,一直到最後一列。編譯器若知道總行數,就知道每一列在哪裡結束,下一列在哪裡開始。且初始化後元素總數既可得知,總列數就也可得知。因此,綜合上述,在宣告一併初始化時總列數是可以省略的 ( 但宣告時未一併初始化,總列數則不可省略 )。
上面的宣告除了會產生了一個二維陣列外,還會產生一組常數指標 my_2D_array 與常數指標陣列 my_2D_array[2] (內含兩個常數指標元素 my_2D_array[0] 與 my_2D_array[1]),如圖五所示。
其中的 my_2D_array 指向 my_2D_array[0] 的位址,my_2D_array[0] 指向 my_2D_array[0][0] 的位址,my_2D_array[1] 指向 my_2D_array[1][0] 的位址。
因此,若要存取 my_2D_array 第 i 列第 j 行的元素 (i 與 j 為正整數,且 $0 \le i \lt $ 總列數,以及 $0 \le j \lt $ 總行數),除了可以 my_2D_array[ i ][ j ] 進行存取外,也可透過指標 *my_2D_array[ i ] 或 *(*(my_2D_array + i) + j) 來進行存取。
其實二維陣列若透過指標存取的運作概念,其實可視為延伸自一維陣列。還記得若存在有一個一維陣列
A[5]
則陣列名稱 A 其實是一個常數指標,其內容值為該陣列的起始位址。陣列第 i 個元素的位址為 A + i,陣列第 i 個元素的內容值則為 *(A+i)。接下來我們來看二維陣列,若存在有一個二維陣列
B[2][4]
延伸自一維陣列的概念,B[2] 是指向 B[2][4] 的常數指標陣列,而 B 是 B[2] 這個常數指標陣列的常數指標,其內容值為常數指標陣列 B[2] 的起始位址;而常數指標陣列 B[2] 內的 B[0] 元素也是常數指標,其內容值為 B[2][4] 陣列第 0 列的起始位址,而 B[2] 內的常數指標 B[1] 元素,其內容值為 B[2][4] 陣列第 1 列的起始位址。
因此,若要存取該陣列第 i 列 (row) 第 j 行 (column) 的元素值,透過指標方式可以以
*(B[i] + j)
或
*(*(B + i) + j)
方式存取。換言之,B[i] 或 *(B + i ) 先找到元素所屬列的位址,接著 B[i] + j 或 *(B + i ) + j 再找到元素所屬列中的所屬行位址。最後 *(B[i] + j ) 或 *(*(B + i ) + j) 就可存取第 i 列 (row) 第 j 行 (column) 元素值。
圖五 二維陣列 my_2D_array 的常數指標指向位址關係圖
範例程式三
#include <iostream>
using namespace std;
int main()
{
int my_2D_array[2][3] = {{10, 20, 30}, {40, 50, 60}};
int i, j;
for (i = 0; i < 2; i++)
for (j = 0; j < 3; j++)
cout << "my_2D_array[" << i<< "][" << j <<" ]= " << my_2D_array[i][j] << "\n";
cout << "-----------------------------\n";
for (i = 0; i < 2; i++)
for (j = 0; j < 3; j++)
cout << "*(*(my_2D_array + " << i << ") +" << j << ") = " << *(*(my_2D_array + i) + j) << "\n";
cout << "-----------------------------\n";
for (i = 0; i < 2; i++)
for (j = 0; j < 3; j++)
cout << "*(my_2D_array[" << i << "] + " << j << ") = " << *(my_2D_array[i] + j) << "\n";
return 0;
}
範例程式三輸出
二維陣列作為函數的形式參數
二維陣列同一維陣列,須以參照參數的方式作為函數的形式參數。
例如:
宣告一無回傳值,名為 sum 的函數,資料型態為 double 的二維陣列 A 以參照參數的方式作為函數的形式參數如下
void sum( double A[ ][numCol] );
或
void sum( double [ ][numCol]);
範例程式四
#include <iostream>
using namespace std;
const int NUMBER_OF_ROWS = 2;
const int NUMBER_OF_COLUMNS = 3;
void displayArray(int A[ ][NUMBER_OF_COLUMNS]);
int main()
{
int A[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS] = { 10, 20, 30, 40, 50, 60 };
displayArray(A);
return 0;
}
void displayArray(int A[ ][NUMBER_OF_COLUMNS])
{
for (int i = 0; i < NUMBER_OF_ROWS; i++)
for (int j = 0; j < NUMBER_OF_COLUMNS; j++)
cout << A[i][j] << "\n";
return;
}
範例程式四輸出
三維陣列 (Three Dimensional Arrays)
圖六 三維陣列示意圖
三維陣列的宣告與初始化
三維陣列的宣告方式如下:
資料型態 陣列名稱[總層數][總列數][總行數];
例如:
以下這些敘述可宣告並初始化同樣的二維陣列:
int my_3D_array [2][3][3] = { { { 10, 12, 17 }, { 24, 13, 64 }, { 44, 54, 22 } }, // 第一層的 3 $\times $ 3 表格
{ { 8, 33, 14 }, { 28, 95,88 }, { 17, 61, 33} } }; // 第二層的 3 $\times $ 3 表格
或
int my_3D_array [ ][3][3] = { { { 10, 12, 17 }, { 24, 13, 64 }, { 44, 54, 22 } },
{ { 8, 33, 14 }, { 28, 95,88 }, { 17, 61, 33} } };
其中可注意到,總層數在宣告一併初始化時是可以省略,但總列數總行數不可省略。
其記憶體配置如圖七所示。
圖七 三維陣列記憶體配置
如圖八所示,上面的宣告除了會產生了一個三維陣列外,還會產生一個常數指標 my_3D_array 以及二個常數指標陣列 my_3D_array[2] ( 內含常數指標元素 my_3D_array[0] 與 my_3D_array[1] ) 與 my_3D_array[2][3] ( 內含常數指標元素 my_3D_array[0][0] 、my_3D_array[0][1]、my_3D_array[0][2]、my_3D_array[1][0]、my_3D_array[1][1] 以及 my_3D_array[1][2] )。
圖八 三維陣列 my_3D_array 的常數指標指向位址關係圖
其中的 my_3D_array 指向 my_3D_array[0] 的位址,my_3D_array[0] 指向 my_3D_array[0][0]的位址;my_my_3D_array[1] 指向 my_3D_array[1][0]的位址;my_3D_array[0][0] 指向 my_3D_array[0][0][0] 的位址;my_3D_array[0][1] 指向 my_3D_array[0][1][0] 的位址...my_3D_array[1][2] 指向 my_3D_array[1][2][0] 的位址。
因此,若要存取 my_2D_array 第 i 列第 j 行的元素 (i 與 j 為正整數,且 $0 \le i \lt $ 總列數,以及 $0 \le j \lt $ 總行數),除了可以 my_2D_array[ i ][ j ] 進行存取外,也可透過指標 *my_2D_array[ i ] 或 *(*(my_2D_array + i) + j) 進行存取。
若宣告一個已知資料型態的三維陣列 A[NumberOfDepths][NumberOfRows][NumberOfColumns],則可以 A[k][i][j] ( k、i 與 j 為正整數,且 0 $\leq$ k $\lt $ NumberOfDepths, 0 $\leq$ i $\lt $ NumberOfRows, 0 $\leq$ j $\lt $ NumberOfColumns) 對位置 (k, i, j) 的元素進行存取。但是若想透過指標存取,其運作類似一、二維陣列,概念說明如下:
設三維陣列 C[2][3][3] 已宣告,記憶體除了會產生 C[2][3][3] 陣列外,還會產生一個常數指標與兩個常數指標陣列,分別是 C、C[2] 以及 C[2][3]。
陣列名稱 C 是一個常數指標,其內容值為常數指標陣列 C[2] 的起始位址,C[2] 陣列中的 C[0] 的內容值為常數指標陣列 C[2][3] 中元素 C[0][0] 的起始位址;C[2] 陣列中的 C[1] 的內容值為陣列 C[2][3] 中 C[1][0] 的起始位址;C[2][3] 中 C[0][0] 的內容值為 C[0][0][0] 的起始位址,C[0][1] 的內容值為 C[0][1][0] 的起始位址,C[2][3] 中 C[0][1] 的內容值為 C[0][1][0] 的起始位址,C[0][1] 的內容值為 C[0][1][0] 的起始位址,...,C[1][2] 的內容值為 C[1][2][0] 的起始位址。
因此,若要存取該陣列第 k 層 (depth) 第 i 列 (row) 第 j 行 (column) 元素值 ( k、i 與 j 皆為正整數,且$0 \le k \lt $ 總層數, $0 \le i \lt $ 總列數,以及 $0 \le j \lt $ 總行數 ),透過指標方式可以以
*(*(C[k] + i) + j)
或
*(C[k][i] + j)
或
*(*(*(C + k) + i) + j)
這三種方式存取。換言之,C[k] 或 *(C + k ) 先找到元素所屬層的起始位址,C[k] + i 或 C[k][i] 或 *(C + k ) + i 再找到元素所屬層中的列位址,接著 (C[k] + i ) + j 或 C[k][i] + j 或 *(*(C + k ) + i) + j 就可找到第 k 層中的第 i 列第 j 行的元素位址。最後 *((C[k] + i ) + j) 或 *(C[k][i] + j) 或 *(*(*(C + k ) + i) + j) 就可對該位置的元素進行存取。
範例程式五
#include <iostream>
using namespace std;
int main()
{
int my_3D_array[ ][3][3] = { { { 10, 12, 17 }, { 24, 13, 64 }, { 44, 54, 22 } },
{ { 8, 33, 14 }, { 28, 95,88 }, { 17, 61, 33} } };
cout << " my_3D_array[1][1][2] = " << my_3D_array[1][1][2] << "\n";
cout << " *(*(my_3D_array[1] + 1) + 2) = " << *(*(my_3D_array[1] + 1) + 2) << "\n";
cout << " *(my_3D_array[1][1] + 2) = " << *(my_3D_array[1][1] + 2) << "\n";
cout << " *(*(*(my_3D_array + 1) + 1) + 2) = " << *(*(*(my_3D_array + 1)+1)+2) << "\n";
}
範例程式五輸出
三維陣列作為函數的形式參數
三維陣列同一維、二維陣列,須以參照參數的方式作為函數的形式參數。
例如:
宣告一無回傳值,名為 sum 的函數,資料型態為 double 的三維陣列 A 以參照參數的方式作為函數的形式參數如下
void sum( double A[ ][numRow][numCol]);
或
void sum( double [ ][numRow][numCol]);
範例程式六
#include <iostream>
using namespace std;
const int NUMBER_OF_DEPTHS = 2;
const int NUMBER_OF_ROWS = 3;
const int NUMBER_OF_COLUMNS = 3;
void displayArray(int A[ ][NUMBER_OF_ROWS][NUMBER_OF_COLUMNS]);
int main()
{
int A[NUMBER_OF_DEPTHS][NUMBER_OF_ROWS][NUMBER_OF_COLUMNS] = { { { 10, 12, 17 },
{ 24, 13, 64 }, { 44, 54, 22 } },{ { 8, 33, 14 }, { 28, 95,88 }, { 17, 61, 33} } };
displayArray(A);
return 0;
}
void displayArray(int A[ ][NUMBER_OF_ROWS][NUMBER_OF_COLUMNS])
{
for (int k = 0; k < NUMBER_OF_DEPTHS; k++)
for (int i = 0; i < NUMBER_OF_ROWS; i++)
for (int j = 0; j < NUMBER_OF_COLUMNS; j++)
cout << A[k][i][j] << "\n";
return;
}
範例程式六輸出
三維陣列以上的多維陣列 (Multidimensional Arrays)
N 維陣列的宣告方式如下:
資料型態 陣列名稱[正整數常量表示式_1][正整數常量表示式_2]﹒﹒﹒[正整數常量表示式_N];
N 維陣列內元素的存取方式如下:
陣列名稱[正整數常量表示式_1][正整數常量表示式_2]﹒﹒﹒[正整數常量表示式_N];


.png)
%E6%93%8D%E4%BD%9C%E7%B5%90%E6%9E%9C%20.png)



%E6%93%8D%E4%BD%9C%E7%B5%90%E6%9E%9C%20.png)
%E6%93%8D%E4%BD%9C%E7%B5%90%E6%9E%9C%20.png)



%E6%93%8D%E4%BD%9C%E7%B5%90%E6%9E%9C%20.png)
%E6%93%8D%E4%BD%9C%E7%B5%90%E6%9E%9C%20.png)
留言
張貼留言