為什麼需要數組?
處理同類型批量數據(如 50 個學生成績)時,定義 50 個變量不現實。數組提供連續內存塊,統一管理,下標訪問。
一維數組
定義與初始化
int scores[5]; // 未初始化,值不定
int scores[5] = {85, 90, 78, 92, 88}; // 完全初始化
int scores[] = {85, 90, 78, 92, 88}; // 自動推導長度=5
int scores[10] = {85, 90}; // 部分初始化,餘補0
遍歷與計算
int sum = 0;
for (int i = 0; i < 5; i++) sum += scores[i];
double avg = (double)sum / 5; // 強制浮點除法
二維數組:矩陣/表格數據
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 訪問:matrix[row][col]
// 內存佈局:行優先連續存儲
字符串與字符數組:關鍵區別
| 特性 | 字符數組 | 字符串字面量 |
| 定義 | char str[] = {'a','b','c'} | char *str = "abc" 或 char str[] = "abc" |
| 結束符 | 無自動 \0 | 自動包含 \0 |
| 長度 | 至少 = 字符數 | 至少 = 字符數 + 1 |
| 可修改性 | 棧上數組可修改 | 字面量常量區,**不可修改** |
| 比較 | strcmp 比較內容 | == 比較地址(錯誤用法) |
正確字符串操作
#include <string.h>
char a[20] = "hello";
char b[] = "world";
strcpy(a, b); // a = "world" (覆蓋)
strcat(a, "!"); // a = "world!"
int cmp = strcmp(a, b); // 0:相等 >0:a大 <0:a小
size_t len = strlen(a); // 不含 \0 的長度
常用字符串函數速查
| 函數 | 作用 | 注意 |
strcpy(dest, src) | 複製 | dest 必須足夠大 |
strncpy(dest, src, n) | 複製前 n 字符 | 不保證 \0 結尾 |
strcat(dest, src) | 追加 | dest 需有空間 |
strcmp(s1, s2) | 比較 | 返回 int,非布爾 |
strlen(s) | 長度 | 返回 size_t,不含 \0 |
strchr(s, c) | 查找字符首次出現 | 返回指針或 NULL |
strstr(s1, s2) | 查找子串 | 返回指針或 NULL |
數組長度計算技巧
int arr[10];
size_t len = sizeof(arr) / sizeof(arr[0]); // 10
// 函數參數退化為指針,sizeof 失效!
void foo(int arr[]) {
// sizeof(arr) == sizeof(int*) == 4/8
// 必須額外傳長度:void foo(int arr[], size_t len)
}
避坑指南
- 越界訪問: C 不檢查下標,
arr[10] 訪問第11個元素 → UB
- 字符串未以 \0 結尾:
strcpy/strlen/printf("%s") 會越界讀取
- 字面量修改:
char *s = "abc"; s[0] = 'x'; → 段錯誤
- 返回局部數組指針: 函數棧幀銷毀,指針失效
- sizeof 指針誤用: 函數參數數組退化為指針,
sizeof 算不出長度
練習建議
- 實現字符串反轉、查找子串、分割字符串
- 用二維數組實現矩陣加法/乘法/轉置
- 模擬簡易通訊錄:結構體數組 + 字符串操作
- 閱讀
string.h 實現源碼(如 glibc)理解優化技巧