C 語言陣列與字串(Arrays & Strings)

陣列 (Arrays)

當我們需要儲存多個相同型態的資料時(例如全班 50 個人的成績),如果宣告 50 個變數會非常麻煩。這時我們可以使用 陣列

1. 宣告與初始化

// 宣告一個可以放 5 個整數的陣列
int numbers[5]; 

// 宣告並初始化內容
int scores[5] = {80, 90, 75, 60, 100};

// 如果有提供初始值,也可以省略大小,編譯器會自動計算
int primes[] = {2, 3, 5, 7, 11}; // 大小自動設為 5

2. 存取陣列元素

我們使用 索引 (Index) 來存取陣列中的元素。注意:陣列索引是從 0 開始的

#include <stdio.h>

int main() {
    int arr[3] = {10, 20, 30};

    printf("第一個元素: %d\n", arr[0]); // 印出 10
    
    // 修改元素
    arr[2] = 50;
    
    // 遍歷陣列
    for(int i = 0; i < 3; i++) {
        printf("%d ", arr[i]);
    }
    
    return 0;
}
危險:C 語言不會檢查陣列是否越界。如果你存取 arr[100],程式可能會讀到垃圾值或直接崩潰 (Segmantation Fault)。我們必須自己確保索引在有效範圍內 (0 到 size-1)。

3. 多維陣列 (Multi-dimensional Arrays)

C 語言支援多維陣列,最常見的是二維陣列 (矩陣)。

// 宣告一個 2x3 的二維陣列 (2 列 3 行)
int matrix[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};

// 存取
printf("%d", matrix[0][1]); // 輸出 2 (第一列第二個)

// 遍歷
for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
        printf("%d ", matrix[i][j]);
    }
    printf("\n");
}

字串 (Strings)

在 C 語言中,並沒有獨立的 String 型態。字串其實就是以 null 字元 (\0) 結尾的字元陣列

1. 宣告字串

// [方法 1] 逐字元宣告,記得手動加 \0
char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

// [方法 2] 使用字串字面常數 (常用),編譯器會自動加 \0
char str2[] = "Hello"; 
// str2 的實際大小是 6 bytes ('H','e','l','l','o','\0')

2. 緩衝區溢位 (Buffer Overflow)

這字串處理最常見也最嚴重的安全漏洞。當你試圖把一個長字串塞進一個短陣列時,就會發生溢位,覆蓋掉相鄰記憶體的資料。

char buffer[5]; // 只能存 4 個字元 + 1 個 \0
// strcpy(buffer, "Hello World"); // 危險!"Hello World" 長度大於 5,會造成溢位

3. 安全的字串函式 (Safe Functions)

為了避免溢位,C 語言標準函式庫 (string.h) 提供了限制長度的版本。

危險 (Unsafe)安全建議 (Safe Alternative)說明
strcpy(dest, src)strncpy(dest, src, n)複製時指定最大長度 n
strcat(dest, src)strncat(dest, src, n)串接時指定最大長度 n
sprintf(buf, fmt...)snprintf(buf, n, fmt...)格式化輸出時指定緩衝區大小
gets(buf)fgets(buf, n, stdin)絕對不要用 gets,它無法防止溢位

最佳實踐範例

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

int main() {
    char src[] = "This is a very long string";
    char dest[10]; // 空間不夠

    // [不安全] strcpy(dest, src); -> 這裡會爆掉

    // [安全] 使用 strncpy
    // sizeof(dest) - 1 是為了預留空間給 \0
    strncpy(dest, src, sizeof(dest) - 1);
    
    // strncpy 不保證補上 \0,如果是截斷的情況,須手動補上
    dest[sizeof(dest) - 1] = '\0';

    printf("Dest: %s\n", dest); // 輸出: This is a

    return 0;
}

4. 傳遞陣列給函式

當陣列傳入函式時,它會退化 (Decay) 成指標。因此函式內部無法知道陣列的長度(sizeof 會失效)。

最佳實踐:一定要多傳一個參數 size

// [錯誤] 這樣算不出長度
void print_array_wrong(int arr[]) {
    // sizeof(arr) 會是 8 (64-bit 指標的大小),而不是陣列總大小
}

// [正確] 明確傳入長度
void print_array(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
}

陣列其實就是一連串記憶體空間。謹慎處理邊界與字串長度,是寫出安全 C 程式的第一步。