字串沒有了
一段文字,其實是一排字元加一個結束記號。 · ⏱ 約 35 分鐘
你會學到三件事
- C 的字串就是
char 陣列
- 結尾那個看不見的
\0 才是「字串結束」的定義
strlen / strcpy / strcmp——以及它們為什麼危險
開始之前
C++ 那邊:
string s = "hello";
s = s + "!";
cout << s.size() << endl;
而 C 沒有 string。一個字都沒有。
一、它其實一直都是一排字元
char s[20] = "hello";
記憶體裡長這樣:
h e l l o \0 ? ? ...
[0] [1] [2] [3] [4] [5] [6] [7] 共 20 格
⚠️ 第 6 格那個 \0 是編譯器幫你放的。
它是一個值為 0 的字元,意思是「字串到這裡結束」。
螢幕上看不見它,而所有處理字串的函式都靠它。
C 的字串沒有「長度」這個欄位。
長度是每次現數的——數到 \0 為止。
所以:
|
|
C++ 的 s.size() |
O(1)——它記著長度 |
C 的 strlen(s) |
O(n)——它每次從頭數 |
⚠️ 所以 for (int i = 0; i < strlen(s); i++) 是一個經典的效能陷阱:
它每一圈都重數一次。
二、為什麼要留 20 格
char s[20] = "hello";
"hello" 只要 6 格(含 \0),而我們宣告了 20。
因為陣列的大小不能變。之後想接東西上去,空間得先留好。
⚠️ 留不夠的話,寫下去會蓋到別人的記憶體,而 C 不會攔你。
那叫緩衝區溢位,是真實世界裡最常見的一類安全漏洞。
三、三個函式
printf("%d\n", strlen(s)); // 5 ——數到 \0
char t[20];
strcpy(t, s); // 把 s 複製到 t
printf("%d\n", strcmp(s, t)); // 0 ——一樣就是 0
|
做什麼 |
⚠️ 危險在哪 |
strlen(s) |
數長度 |
沒有 \0 的話它會一直數下去 |
strcpy(t, s) |
把 s 複製到 t |
不檢查 t 夠不夠大 |
strcmp(a, b) |
比較 |
一樣回 0,不是回 1 |
🔴 strcmp 回 0 代表「一樣」
這違反直覺——大部分「比較」函式回 true 代表一樣,而 strcmp 相反。
它回的是「差多少」:
0 一樣
負數 a 排在 b 前面
正數 a 排在 b 後面
所以判斷相等要寫 if (strcmp(a, b) == 0)。
⚠️ 寫成 if (strcmp(a, b)) 的話意思正好相反。
🔴 而 == 不能用
if (s == t) // 🔴 這是在比【位址】,不是比內容
s 和 t 是兩個陣列,== 比的是它們在記憶體裡的位置——
永遠不相等。這個錯不會報錯,只會永遠走 else。
完成的樣子
int main() {
char s[20] = "hello";
printf("%d\n", strlen(s));
char t[20];
strcpy(t, s);
printf("%d\n", strcmp(s, t));
return 0;
}
換你了
把 t 的大小改成 char t[3],執行看看。
⚠️ 它可能還是印出 0——而那正是最可怕的地方:
壞掉的程式不一定會壞給你看。
這一課你做了什麼
- 你知道 C 的字串是
char 陣列加一個 \0
- 你用
strlen / strcpy / strcmp 做了三件事
- 你知道
strcmp 回 0 是一樣,而 == 比的是位址
如果卡住了
| 你看到 |
多半是因為 |
strlen 得到一個很大的數字 |
沒有 \0——多半是 strcpy 到一個太小的陣列 |
| 兩個字串明明一樣卻判定不同 |
用了 ==。要用 strcmp(...) == 0 |
| 判斷相等的邏輯反了 |
strcmp 回 0 是一樣。if (strcmp(a,b)) 是「不一樣」 |
| 程式莫名其妙壞掉 |
緩衝區溢位。目標陣列留大一點 |
在編輯器打開這一課 →