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

PUBLISHED ON JUL 5, 2018 — PROGRAMMING

陣列是一種線性 (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.

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

TAGS: C 語言, ARRAY
comments powered by Disqus