C語言陣列長度取得完整指南:從基礎概念到實用技巧

1. 前言

程式語言「C語言」因其簡潔性與高效能,被廣泛應用於系統開發、嵌入式系統等多個領域。其中,「陣列」是一種用來集中管理資料的重要資料結構,在許多程式中被頻繁使用。

本文將詳細解說在 C 語言中「取得陣列長度的方法」。特別針對初學者容易遇到的難點,從基礎到應用進行細緻說明,幫助你扎實掌握正確取得陣列長度的技巧。

2. 陣列的基本概念

什麼是陣列?

陣列是一種可以將相同資料型態的值集中管理的資料結構。在處理多筆相同型態的資料時非常方便,並會在記憶體中連續配置空間。

陣列的用途

  1. 資料批次處理 – 適用於需要集中管理同類型資料的情況,例如學生分數、感測器資料等。
  2. 重複處理的應用 – 因為可透過迴圈依序存取,因此適合在相同處理需重複執行時使用。
  3. 記憶體管理 – 使用連續的記憶體空間,存取速度快且效率高。

陣列的運作原理

陣列透過索引(index,下標)來存取各個元素。在 C 語言中,索引從 0 開始,最後一個元素可透過「陣列大小 – 1」來存取。

範例:

int numbers[5] = {10, 20, 30, 40, 50};
printf("%d\n", numbers[0]); // 輸出 10
printf("%d\n", numbers[4]); // 輸出 50

在此範例中,陣列 numbers 存放了 5 個整數,並可透過索引存取各元素。

侍エンジニア塾

3. 陣列的宣告與初始化

宣告陣列的方法

在 C 語言中,陣列的宣告方式如下:

型別 陣列名稱[大小];

具體範例:

int scores[10]; // 整數型陣列,元素數量為 10

此範例中,宣告了一個整數型(int)的陣列 scores,並配置了 10 個元素的記憶體空間。

陣列的初始化

宣告陣列時可以同時進行初始化。

  1. 明確初始化的例子
int values[5] = {1, 2, 3, 4, 5};
  1. 僅初始化部分元素的例子
int data[5] = {10, 20}; // 其他元素自動初始化為 0
  1. 省略大小的初始化例子
int numbers[] = {10, 20, 30}; // 自動計算元素數量為 3

未初始化的注意事項

若在使用陣列前未初始化,可能會存有不可預測的值(隨機記憶體資料)。因此建議在需要時進行初始化。

4. 取得陣列長度(元素數)的方法

在 C 語言中,正確取得陣列的長度(元素數)非常重要。尤其在進行迴圈處理或資料管理時,需要掌握陣列的大小。本節將詳細說明具體的取得方法與注意事項。

4.1 使用 sizeof 運算子取得長度

最常見的方法是使用 sizeof 運算子。sizeof 會回傳資料型態或變數的記憶體大小(位元組數)。

基本程式範例

#include <stdio.h>

int main() {
    int array[5] = {10, 20, 30, 40, 50};
    int length = sizeof(array) / sizeof(array[0]); // 計算陣列元素數

    printf("陣列長度: %d\n", length); // 輸出: 5
    return 0;
}

重點說明:

  • sizeof(array) – 取得整個陣列的位元組大小。
  • sizeof(array[0]) – 取得第一個元素的位元組大小。
  • 用陣列總大小除以單一元素大小,即可計算出元素數量。

4.2 將陣列傳入函式時的注意事項

當陣列作為引數傳入函式時,會轉換成「指標」。因此,sizeof 運算子此時回傳的是指標的大小(多數情況下為 4 或 8 位元組),而不是整個陣列的大小。

錯誤範例

#include <stdio.h>

void printArrayLength(int arr[]) {
    printf("大小: %ld\n", sizeof(arr) / sizeof(arr[0])); // 無法正確運作
}

int main() {
    int array[5] = {1, 2, 3, 4, 5};
    printArrayLength(array);
    return 0;
}

解決方法
在傳遞陣列時,同時傳遞長度作為另一個引數。

修正版範例

#include <stdio.h>

void printArrayLength(int arr[], int length) {
    printf("陣列長度: %d\n", length);
}

int main() {
    int array[5] = {1, 2, 3, 4, 5};
    int length = sizeof(array) / sizeof(array[0]); // 計算元素數
    printArrayLength(array, length);
    return 0;
}

5. 取得字串陣列的長度

在 C 語言中,字串也是以陣列形式儲存。但與一般數值陣列不同,字串是 char 型態的陣列,且在結尾會自動加上特殊的終止字元 '\0'。本節將解說取得字串陣列長度的方法與注意事項。

5.1 字串與陣列的關係

字串是由多個字元組成的 char 型態陣列。範例如下:

char str[] = "Hello";

此時 str 在記憶體中的配置如下:

Hello‘\0’

重點說明:

  • '\0' 是終止字元,表示字串的結尾。
  • 陣列的大小會包含這個終止字元,因此此例中大小為 6 位元組。

5.2 使用 strlen 函式取得字串長度

要取得字串長度(字元數),可使用標準函式庫的 strlen 函式。

程式範例:

#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello";
    printf("字串長度: %ld\n", strlen(str)); // 輸出: 5
    return 0;
}

注意事項:

  • strlen 計算時不包含終止字元 '\0'

5.3 與 sizeof 的差異

取得字串長度時,雖然也可以使用 sizeof,但結果與 strlen 不同。

程式範例:

#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello";
    printf("sizeof: %ld\n", sizeof(str));  // 輸出: 6(包含終止字元)
    printf("strlen: %ld\n", strlen(str)); // 輸出: 5(不含終止字元)
    return 0;
}

差異重點:

  • sizeof 回傳的是整個陣列的大小(含終止字元)。
  • strlen 回傳的是實際字元數(不含終止字元)。

6. 可變長陣列(VLA)的使用

在 C 語言中,自 C99 標準開始引入了可變長陣列(VLA: Variable Length Array)。使用此功能可以在執行時決定陣列的大小,而不必在編譯時固定。本節將詳細解說 VLA 的特點、使用方法與注意事項。

6.1 什麼是可變長陣列(VLA)?

一般的陣列屬於靜態陣列,大小必須在編譯時決定。而 VLA 則是一種可在執行時依輸入或計算結果動態決定大小的陣列。

傳統靜態陣列範例:

int arr[10]; // 大小在編譯時固定

可變長陣列(VLA)範例:

int size;
scanf("%d", &size); // 執行時決定大小
int arr[size];      // 執行時配置的陣列

6.2 VLA 的基本用法

以下範例會依使用者輸入的大小建立陣列並輸出內容:

程式範例:

#include <stdio.h>

int main() {
    int n;

    printf("請輸入陣列大小: ");
    scanf("%d", &n); // 執行時決定大小

    int arr[n]; // 宣告 VLA

    // 輸入資料
    for (int i = 0; i < n; i++) {
        arr[i] = i + 1;
    }

    // 輸出陣列內容
    printf("陣列元素: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}

輸出範例:

請輸入陣列大小: 5
陣列元素: 1 2 3 4 5

重點說明:

  1. VLA 可以在執行時決定大小,資料管理更靈活。
  2. 即使程式開始時未知大小,也可以在中途依輸入決定。

7. 關於陣列長度的注意事項

在 C 語言中,管理陣列的大小至關重要。如果陣列長度處理錯誤,不僅會造成程式執行異常,甚至可能帶來嚴重的安全風險。本節將介紹重要注意事項與安全編碼方法。

7.1 防止陣列越界存取

由於陣列是固定大小的資料結構,如果超過索引範圍存取,可能導致不可預期的行為或程式崩潰。

錯誤範例(越界存取)

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};

    for (int i = 0; i <= 5; i++) { // 條件錯誤,會越界
        printf("%d\n", arr[i]);
    }

    return 0;
}

正確作法:

  • 設定正確的迴圈條件
for (int i = 0; i < 5; i++) { // 正確條件
    printf("%d\n", arr[i]);
}
  • 使用動態計算的長度
int length = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < length; i++) {
    printf("%d\n", arr[i]);
}

7.2 緩衝區溢位(Buffer Overflow)的風險

管理陣列大小不當最常見的問題之一就是緩衝區溢位,即寫入超過陣列範圍的資料,導致覆寫其他記憶體區域。

錯誤範例:

#include <stdio.h>
#include <string.h>

int main() {
    char buffer[10]; // 陣列大小 10
    strcpy(buffer, "This string is too long!"); // 超過大小
    printf("%s\n", buffer);
    return 0;
}

安全作法:

  • 使用安全版本的函式
strncpy(buffer, "This string is too long!", sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0'; // 確保結尾有終止字元

8. 總結

本文詳細解說了 C 語言中關於「陣列長度」的基礎到進階知識。陣列雖然好用,但若操作不當,可能引發錯誤或安全漏洞。以下是重點回顧與後續建議。

8.1 本文回顧

  1. 陣列的基本概念與宣告、初始化
  • 陣列會連續儲存相同型態的元素,宣告時需指定大小。
  • 初始化時可省略大小,由編譯器自動計算。
  1. 取得陣列長度的方法
  • 靜態陣列可用 sizeof 計算長度。
  • 函式傳參時需額外傳遞長度。
  1. 取得字串陣列長度的方法
  • 字串用 strlen 取得字元數,理解與 sizeof 的差異。
  • 多維字串需分別管理每列長度。
  1. 可變長陣列(VLA)的使用
  • 可在執行時決定大小,但要注意堆疊溢位與編譯器支援性。
  • 必要時可考慮使用 malloc 動態配置。
  1. 安全編碼與長度管理
  • 避免越界與緩衝區溢位,使用安全函式。
  • 動態記憶體需搭配釋放使用。

8.2 後續建議

  1. 動手實作範例
  • 編譯並執行文中範例,觀察結果。
  1. 挑戰進階題
  • 嘗試撰寫多維陣列與指標的應用程式。
  1. 持續實踐安全編碼
  • 養成考慮安全性與錯誤處理的習慣。

8.3 最後

陣列是 C 語言的基礎,同時具有廣泛應用範圍。請善用本文內容,編寫出安全且高效的程式。

常見問題(FAQ)

Q1: 為什麼使用 sizeof 在函式內取得陣列長度會失敗?

A:
sizeof 在陣列直接定義於作用域內時,會回傳整個陣列的大小。但當陣列作為引數傳入函式時,會退化成指標,只保留陣列首位址。因此 sizeof 取得的是指標大小(4 或 8 位元組),無法得到正確元素數。

解決方法:
在呼叫函式時一併傳遞陣列長度。

void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d\n", arr[i]);
    }
}

int main() {
    int array[5] = {1, 2, 3, 4, 5};
    printArray(array, sizeof(array) / sizeof(array[0])); // 傳遞長度
    return 0;
}

Q2: 取得字串長度時該用 sizeof 還是 strlen

A:
依用途而定:

  • sizeof: 回傳整個陣列大小(含終止字元 '\0'),適合檢查緩衝區大小。
  • strlen: 回傳實際字元數(不含 '\0'),適合取得字串長度。

範例:

char str[] = "Hello";
printf("%ld\n", sizeof(str));  // 輸出: 6(含 '\0')
printf("%ld\n", strlen(str)); // 輸出: 5

Q3: VLA 和 malloc 動態配置哪個比較好?

A:
取決於需求:

  • VLA 特點: 語法簡單,執行時可決定大小,但使用堆疊記憶體,過大會導致堆疊溢位,且 C11 之後部分編譯器不支援。
  • malloc 特點: 使用堆記憶體,可處理大型資料,需自行釋放記憶體,但可移植性高。

Q4: 如果忘記釋放動態記憶體會怎樣?

A:
會發生記憶體洩漏(Memory Leak),累積後可能造成系統變慢或程式崩潰。

解決方法:
動態配置後,使用完必須呼叫 free() 釋放。

int *arr = malloc(10 * sizeof(int));
if (arr == NULL) {
    printf("配置失敗\n");
    return 1;
}

free(arr); // 釋放記憶體

Q5: 如何防止緩衝區溢位?

A:
請注意以下事項:

  1. 檢查輸入長度 – 限制輸入資料不超過陣列大小。
  2. 使用安全函式 – 例如用 strncpy 取代 strcpy
  3. 預留終止字元空間 – 陣列大小應包含 '\0'
  4. 邊界測試 – 執行測試檢查極端情況。

總結

本 FAQ 針對陣列長度取得的常見疑問與注意事項提供了具體解答,請搭配本文內容一起參考,幫助你撰寫安全且高效的程式。