Semorphe

字串沒有了

一段文字,其實是一排字元加一個結束記號。 · ⏱ 約 35 分鐘

你會學到三件事

  1. C 的字串就是 char 陣列
  2. 結尾那個看不見的 \0 才是「字串結束」的定義
  3. strlen / strcpy / strcmp,以及它們為什麼危險

開始之前

C++ 那邊:

string s = "hello";
s = s + "!";
cout << s.size() << endl;

而 C 沒有 string。一個字都沒有。

🟢 先把編輯器打開,跟著下面一起做:跟著做

一、它其實一直都是一排字元

C 沒有 string,一段文字就是一排 char:

1char s[20] = "hello";
20"hello"建立char(字元)陣列s大小初始值1

記憶體裡長這樣:

 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 格

那個 20 是你自己決定的上限:

1char s[20] = "hello";
20"hello"建立char(字元)陣列s大小初始值1

"hello" 只要 6 格(含 \0),而我們宣告了 20。

因為陣列的大小不能變。之後想接東西上去,空間得先留好。

⚠️ 留不夠的話,寫下去會蓋到別人的記憶體,而 C 不會攔你。 那叫緩衝區溢位,是真實世界裡最常見的一類安全漏洞。

三、三個函式

積木盤上它們寫著「文字長度(…)」「比較文字(…, …)」 「複製文字 … 到 …」,名字就是它們做的事。

長度、複製、比較各有一支:

1printf("%d\n", strlen(s));      // 52char t[20];3strcpy(t, s);4printf("%d\n", strcmp(s, t));   // 0
變數s文字長度()20變數t變數s變數s變數t比較文字(,)格式化輸出 (printf)%d\n,複製文字到建立char(字元)陣列t大小格式化輸出 (printf)%d\n,使用命名空間std引入函式庫cstring(C++ 文字處理)引入函式庫cstdio(C++ 標準輸入輸出)1234
做什麼 ⚠️ 危險在哪
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。

完成的樣子

這一課的參考做法——程式碼每一行的號碼,就印在對應的那塊積木上。

1#include <cstdio>2#include <cstring>3using namespace std;4int main() {5    char s[20] = "hello";6    printf("%d\n", strlen(s));7    char t[20];8    strcpy(t, s);9    printf("%d\n", strcmp(s, t));10    return 0;11}
20"hello"變數s文字長度()20變數t變數s變數s變數t比較文字(,)0回傳格式化輸出 (printf)%d\n,複製文字到建立char(字元)陣列t大小格式化輸出 (printf)%d\n,建立char(字元)陣列s大小初始值定義函式 回傳型別int(整數)main使用命名空間std引入函式庫cstring(C++ 文字處理)引入函式庫cstdio(C++ 標準輸入輸出)12345678910

換你了

把 t 的大小改成 char t[3],執行看看。

⚠️ 它可能還是印出 0,而那正是最可怕的地方: 壞掉的程式不一定會壞給你看。

到編輯器做這一題:練習:把 t 改成 char t[3],看它壞不壞給你看

做一個

印出 "Semorphe" 的長度,再把它一個字一個字倒著印出來。

🔴 char 陣列沒有「反轉」這種東西可以叫,你只能自己用迴圈從後面往前走。 ⚠️ 一個字一個字印要用 %c,不是 %s。

🔴 而這一題示範了 C 字串真正的代價:s 是一排格子, 而「它有多長」這件事不存在那排格子裡。要嘛去數那個 \0,要嘛自己記著。

到編輯器做這一題:做一個:把名字倒著印出來

這一課你做了什麼

如果卡住了

你看到 多半是因為
strlen 得到一個很大的數字 沒有 \0——多半是 strcpy 到一個太小的陣列
兩個字串明明一樣卻判定不同 用了 ==。要用 strcmp(...) == 0
判斷相等的邏輯反了 strcmp 回 0 是一樣。if (strcmp(a,b)) 是「不一樣」
程式莫名其妙壞掉 緩衝區溢位。目標陣列留大一點
在編輯器打開這一課 →