HOME ABOUT CONTACT

C/C++教學: 第十七課 - 陣列(Array)

Rain September 15, 2024
Outline

1. 簡介

2. 整數陣列

3. 字元陣列

4. 二維陣列

簡介top

本篇要來介紹陣列(Array)資料結構,陣列結構是讓設計者向系統要求了一塊連續的記憶體空間,讓設計者可存放一連串的變數,或結構,或物件等等,不管是哪一種型態,這些我們都統一稱為元素(Element)。

由於陣列是一塊連續的記憶體空間,因此它的好處是可快速和方便地存取資料,也有助於硬體執行的效能。 而缺點是它較不能善用或可能產生零碎的記憶體空間,若陣列是用靜態配置的方式, 則它的記憶體空間會是固定的。另外就是陣列的元素只能存放同一種型態,不利變動和靈活使用。

到這裡,你可能還不是很清楚陣列的性質,沒關係,我們可以藉由一些例子和實務經驗來進一步地了解。

整數陣列top

首先,我們先來看看常見範例之一的整數陣列,如前述,陣列中只能存放的元素型態為整數。可以運用這樣的陣列表示一連串的操作指令,又或是已經經過硬體處理運算出來的結果,設計者可以將這一連串的資料進 行驗證。

我們先來看看下面的整數陣列範例:


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個變數的記憶體空間。 如何使用動態配置的方式宣告陣列,我們將在後續篇章會解說。

字元陣列top

介紹完整數陣列之後,我們再來看看常見範例之二的字元陣列。不曉得各位在前面篇章介紹字元時,有沒有疑問是難道程式語言沒有一整句話的資料型態嗎? 都只有單一個字嗎? 答案是有的,我們可以用字元陣列來組成一整句話,而這個我們就稱之為"字串(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)去訪問陣列元素時, 是一個非常重要和基本的概念。

二維陣列top

上面所有的範例,全部展示的都是一維陣列的形式。那麼,我們要如何宣告二維陣列呢?
假設我們將第一個整數範例的四個數值存放到二維陣列中,可以怎麼實現呢? 看看如下範例:


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:

Related Artical List

  1. C/C++教學: 第十五課 - 指標
  2. C/C++教學: 第十六課 - 傳值呼叫、傳址呼叫、傳參呼叫
  3. C/C++教學: 第十八課 - 動態記憶體配置(Dynamic Memory Allocation)

Article List