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 程式的第一步。