C 語言程式設計教學:陣列 (Array)

PUBLISHED ON JUL 5, 2018 — PROGRAMMING
FacebookTwitter LinkedIn LINE Skype EverNote GMail Email Email

    陣列是一種線性 (linear) 且連續的 (contiguous) 容器型別 (或資料結構),陣列包括兩個要素:

    • 陣列中元素 (element) 的資料型別
    • 陣列的大小

    C 語言的陣列元素一定要相同型別。由於 C 的陣列不儲存長度的資訊,程式設計者需預先指定或用另一個變數儲存陣列長度。

    初始化陣列

    宣告陣列的方式如下:

    int main(void) {
        {
            // Declare an array w/o init.
            int arr[5];
        }
        
        {
            // Init an array.
            int arr[5] = {1, 2, 3, 4, 5};
        }
        
        {
            // Init an array.
            int arr[] = {1, 2, 3, 4, 5};
        }
    
        return 0;
    }

    在第一種方式中,我們宣告了一個大小為 5,儲存 int 的陣列 arr,但沒有將陣列初始化,這時候陣列內的值是無意義的垃圾值。

    在第二種方式中,我們除了宣告陣列 arr 外,還將其初始化。

    在第三種方式中,算是稍微簡略的寫法,由編譯器自動推算 arr 的大小。

    但陣列大小為變數時會引發錯誤:

    #include <stddef.h>
    
    int main(void) {
        size_t sz = 5;
            
        // Error.
        int arr[sz] = {1, 2, 3, 4, 5};
    
        return 0;
    }

    錯誤訊息如下:

    error: variable-sized object may not be initialized

    如果要在程式中動態決定陣列的大小,需藉由動態記憶體配置來達成。

    存取陣列元素

    陣列以大於等於 0 的整數為索引,第一個元素位於 0,即 zero-based。見以下實例:

    #include <assert.h>
    
    int main(void) {
        int arr[] = {4, 3, 5, 1, 2};
        
        assert(arr[0] == 4);
        assert(arr[1] == 3);
        assert(arr[2] == 5);
        assert(arr[3] == 1);
        assert(arr[4] == 2);
    
        return 0;
    }

    大部分程式語言的陣列都是 zero-based,寫一陣子程式就會習慣這種方式。一個簡單的想法是將索引值想成偏移值 (offset):

    • 第 1 個元素沒有偏移,故索引值為 0
    • 第 2 個元素偏移 1 格,故索引值為 1
    • 其他元素以此類推

    除了取出值,也可以寫入值。如下例:

    #include <assert.h>
    
    int main(void) {
        int arr[] = {2, 4, 1, 3};
        
        assert(arr[0] == 2);
        assert(arr[1] == 4);
        assert(arr[2] == 1);
        assert(arr[3] == 3);
     
        // Mutate the value on arr[1]
        arr[1] = 99;
        
        assert(arr[0] == 2);
        assert(arr[1] == 99);
        assert(arr[2] == 1);
        assert(arr[3] == 3);
    
        return 0;
    }

    存取超過陣列大小的元素是未定義行為 (undefined behavior),所謂的未定義行為是指未規範在 C 標準的情境,由各個 C 編譯器自行處理。如以下實例:

    #include <stdio.h>
    
    int main()
    {
        int arr[] = {4, 2, 1, 3};
        
        // Undefined behavior.
        printf("%d\n", arr[4]);
        
        return 0;
    }

    在本例中,不論 arr[4] 取得的值是什麼,都沒有實質的意義,只是一些垃圾值。

    走訪陣列

    C 的陣列沒有內建的迭代器 (iterator),直接以索引值走訪即可:

    #include <stddef.h>
    #include <stdio.h>
    
    int main()
    {
        int arr[] = {4, 2, 5, 1, 3};
        
        for (size_t i = 0; i < 5; i++) {
            printf("%d ", arr[i]);
        }
        
        printf("\n");  // Tailing newline.
        
        return 0;
    }

    記算陣列的大小

    C 語言的陣列本身不會儲存陣列的大小,不過可以用 sizeof 計算出來。見下例:

    #include <assert.h>
    #include <stddef.h>
    
    int main(void) {
        int arr[] = {4, 2, 3, 5, 1};
        
        // Calculate the size of arr.
        size_t sz = sizeof(arr) / sizeof(int);
        
        assert(sz == 5);
    
        return 0;
    }

    但這僅限於靜態配置的陣列。比較實用的方法是將陣列大小的資訊另外儲存,如下例:

    #include <stddef.h>
    
    // Array is a dynamic array, i.e. a resizable array.
    typedef struct array Array;
    
    struct array {
        size_t size;
        size_t capacity;
        double *elements;
    };
    
    // Implement the Array here.

    在此例中,我們將陣列包在一個結構中,當我們需要陣列大小時,就取 size 的值即可。上述程式碼的內容超出目前的範圍,相關語法特性會於後文介紹。

    多維陣列

    前述的陣列是一維陣列,C 也支援多維陣列 (multi-dimensional array),如下例:

    #include <assert.h>
    
    int main()
    {
        int mtx[2][3] = {
            {1, 2, 3},
            {4, 5, 6}
        };
        
        assert(mtx[1][2] == 6);
        
        return 0;
    }

    不過,如果我們要用 C 實作數學上的向量 (vector) 或矩陣 (matrix),比較少用這種方法,而會採下列方式來宣告:

    #include <stddef.h>
    
    // Matrix is a 2D matrix.
    typedef struct matrix Matrix;
    
    struct matrix {
        size_t col;
        size_t row;
        double *elements;
    };
    
    // Implement the Matrix here.

    這裡用到結構 (struct) 和指標 (pointer) 等語法,一開始看不懂是正常的,這是學完 C 的核心語法後,開始練習用 C 實作資料結構時才會碰到的情境之一。