本篇要來介紹陣列(Array)資料結構,陣列結構是讓設計者向系統要求了一塊連續的記憶體空間,讓設計者可存放一連串的變數,或結構,或物件等等,不管是哪一種型態,這些我們都統一稱為元素(Element)。
由於陣列是一塊連續的記憶體空間,因此它的好處是可快速和方便地存取資料,也有助於硬體執行的效能。
而缺點是它較不能善用或可能產生零碎的記憶體空間,若陣列是用靜態配置的方式,
則它的記憶體空間會是固定的。另外就是陣列的元素只能存放同一種型態,不利變動和靈活使用。
到這裡,你可能還不是很清楚陣列的性質,沒關係,我們可以藉由一些例子和實務經驗來進一步地了解。
首先,我們先來看看常見範例之一的整數陣列,如前述,陣列中只能存放的元素型態為整數。可以運用這樣的陣列表示一連串的操作指令,又或是已經經過硬體處理運算出來的結果,設計者可以將這一連串的資料進
行驗證。
我們先來看看下面的整數陣列範例:
void main (int argc, char* argv[]) {
int arr[4] = {1, 2, 3, 4};
}
如上範例,我們定義了一個整數陣列,並向系統要求配置一塊可存放4個整數的陣列,初始值分別是1、2、3、4(初始值不一定要有)。
假設這是由某個函式或某個IO接收到的輸入值,現在我們要輸出驗證這段數值可以怎麼做呢? 如下範例:
void main (int argc, char* argv[]) {
int arr[4] = {1, 2, 3, 4};
for (int i = 0 ; i < sizeof(arr)/sizeof(int) ; i++) {
std::cout << "arr[" << i << "] = " << arr[i] << std::endl;
}
}
我們可以用一個 for loop 來訪問陣列中各個元素。先在迴圈中定義欄位定義一個變數 i ,並且初始值為 0 ,因為 C 語言陣列的起始索引(Index)是 0 開始,
接著,我們可以用標準函式庫中的 sizeof 函式來得知我們所定義的陣列 arr 它所占用的記憶體空間大小,並且除以整數型態的記憶體空間大小,即可得到陣列的元素個數,
讓變數(索引) i 在遞增時不會超過陣列的範圍。最後,可以依據個人喜好的方式將各個元素輸出顯示。
到這邊為止,我們所用的就是靜態配置的方式,意思是我們在定義陣列的一開始,就已經向系統要求一塊能存放4個變數的記憶體空間。
如何使用動態配置的方式宣告陣列,我們將在後續篇章會解說。
介紹完整數陣列之後,我們再來看看常見範例之二的字元陣列。不曉得各位在前面篇章介紹字元時,有沒有疑問是難道程式語言沒有一整句話的資料型態嗎? 都只有單一個字嗎?
答案是有的,我們可以用字元陣列來組成一整句話,而這個我們就稱之為"字串(string)",在 C++ 中我們可以直接宣告 string 這樣的資料型態,這是 C++ 延伸出來的資料結構,
我們可以引用 string 的標頭檔後,便可直接寫 std::string 這樣的資料型態來宣告定義字串。然而在 C 裡則是用字元陣列的來完成字串。
我們來看看下面的字元陣列範例:
void main (int argc, char* argv[]) {
char arr[16] = "Hello World";
}
和整數陣列相似,我們宣告了一個字元陣列,代表陣列中能存放的元素只能是字元。這次我們要求了一塊連續記憶體空間可以存放最多16個字元,並且我們賦予其值為 Hello World ,要特別注意的事,當我們在賦予字元初始值時,使用的是單引號,而在給字元陣列或字串初始值時,使用的則是雙引號。
宣告好字元陣列之後,我們要如何輸出顯示整個字元陣列呢? 看看如下範例:
void main (int argc, char* argv[]) {
char arr[16] = "Hello World";
std::cout << arr << std::endl;
printf("%s\n", arr);
}
當我們用 std::cout 直接輸出 arr 陣列時,它相當於下一行的 printf("%s\n", arr) 的輸出方式。
它的原意是: 從 arr 陣列的起始索引位址開始,以字串的方式將整個陣列資料輸出。
由於它是連續的記憶體空間,因此系統可以連貫地訪問全部陣列中的字元,並將其一一輸出。
不管任何型態的陣列,陣列名稱(如上例中的 arr )在 C 語言中本身就代表著陣列的起始索引位址,這需要特別記住,因為這對之後用指標(Pointer)去訪問陣列元素時,
是一個非常重要和基本的概念。
上面所有的範例,全部展示的都是一維陣列的形式。那麼,我們要如何宣告二維陣列呢?
假設我們將第一個整數範例的四個數值存放到二維陣列中,可以怎麼實現呢? 看看如下範例:
void main (int argc, char* argv[]) {
int arr[2][2] = { {1, 2}, {3, 4} };
}
我們宣告了一個 2*2 的二維陣列,並且將四個整數配置到其中,分別是第一列第一行元素為1,第一列第二行元素為2,以此類推。
現在,我們要如何輸出這二維陣列的所有元素呢? 看看下面的範例:
void main (int argc, char* argv[]) {
int arr[2][2] = { {1, 2}, {3, 4} };
for (int i = 0 ; i < sizeof(arr) / sizeof(arr[i]) ; i++) {
for (int j = 0; j < sizeof(arr[i]) / sizeof(int); j++) {
std::cout << "arr[" << i << "][" << j << "] = " << arr[i][j] << std::endl;
}
}
}
這邊我們使用了巢狀迴圈(兩層或兩層以上的迴圈架構)來輸出二維陣列的所有元素,第一層迴圈上限我們一樣用
sizeof 來計算 arr 全部占用的記憶體大小,並且作為被除數,除數是當前列 arr[i] 所佔用的記憶體大小來計算出陣列的列總數。
第二層迴圈上限是用當前列 arr[i] 佔用空間當作被除數,除數各個整數 int 所佔用空間,來計算出一列裡有多少元素(行數)。
這邊的數學計算若感到不是很流順,可以在這裡停一下並且多琢磨一下,順便可以回顧簡介環節所提到的陣列優缺點,
我們在這裡的迴圈設計其實也運用到了其中的特性。
下一篇: 第十八課 - 動態記憶體配置(Dynamic Memory Allocation)
Last updated: