首頁> 現代都市> 目錄頁> 第235章 引號陷阱!趙強的狀態追蹤!解封倒計時!

第235章 引號陷阱!趙強的狀態追蹤!解封倒計時!

2026-08-21 04:01:27 作者: 冷焰天

  五月二十九日。封校第三十七天。

  趙強是被走廊里的喊聲吵醒的。

  "解封快了!連續兩天零新增了!"

  他從上鋪探出頭,看見隔壁寢室幾個人在走廊里蹦來蹦去。

  林遠已經醒了,靠在床頭看論文:"29號如果還是零,30號下午學校會發通知。"

  "那不就是明天?"

  "看數據。"

  蘇明遠在對面床上翻了個身:"三十七天了……我連校門朝哪邊開都快忘了。"

  趙強沒接話。他已經翻身下床,筆記本攤在桌上,翻到第四條。

  字符串閉合。

  他盯著這四個字看了很久。

  前三個規則的難點完全不同。分號檢測靠狀態機區分上下文——一行代碼該不該有分號,取決於當前處於什麼狀態。變量檢測靠作用域樹跨行追蹤——變量在哪裡聲明、在哪裡使用、在哪裡失效。括號匹配靠棧結構處理嵌套——左括號進右括號出,類型必須匹配。

  字符串閉合靠什麼?

  他先寫了一行最簡單的代碼:

  // c

  char *s = "hello world;

  缺了結尾的引號。人眼一掃就知道。但程序怎麼判斷?

  最簡單的思路——數引號。一行里引號出現奇數次就是未閉合。

  他立刻否定了自己。

  // c

  char *s = "he said "hello"";

  三個雙引號,但字符串是閉合的。中間兩個"是轉義的,不算起止標記。

  "數引號不行。得追蹤狀態。"

  他又想到了狀態機。分號檢測的時候林遠教過——五種狀態,根據當前字符決定轉換。但那個狀態機只有五種狀態,轉換條件也簡單。字符串閉合的狀態追蹤要複雜得多。

  他在紙上畫起來。

  

  四個狀態:普通代碼、雙引號字符串內部、單引號字符常量內部、轉義狀態。轉換條件逐一列出——普通代碼遇到"進入字符串,字符串里再遇到"退出。反斜槓進入轉義狀態,轉義完回退到之前的狀態。

  行末判斷:狀態如果還在字符串內部,就是未閉合。

  畫完基本圖,他又想到了續行符:

  // c

  char *s = "hello

  world";

  第一行末尾的反斜槓表示續行。字符串要到第二行末尾的引號才閉合。這種情況第一行行末不能報錯。

  他加了一個續行狀態:行末遇到不檢查閉合,把字符串狀態保留到下一行。

  五個狀態。轉換條件清晰。邊界處理完整。

  然後他列邊界用例。這次他沒急著寫代碼——變量檢測的時候他吃過虧,初版寫完才發現邊界情況沒考慮全。現在他養成了習慣:先把所有要處理的場景列出來,預期輸出想清楚,再動手。

  1. 普通字符串未閉合

  2. 字符常量未閉合

  3. 轉義引號不算閉合

  4. 連續轉義:"pathtofile"

  5. 注釋里的引號:// "這不是字符串"

  6. 字符串里包含單引號:"it's fine"

  7. 續行字符串

  8. 行末只有反斜槓

  八個用例。每個的預期輸出都寫在旁邊。

  代碼結構比分號檢測複雜——轉義狀態需要記住"從哪來的",才能轉義結束後回退。他用了一個prev_state變量記錄進入轉義之前的狀態。


  四十分鐘寫完。

  跑前五個用例。全部通過。

  第六個出了bug。

  "it's fine"——字符串里的單引號被當成字符常量的起始標記,狀態直接亂了。後續的雙引號被匹配進了字符常量,整個判斷崩潰。

  他盯著屏幕看了十幾秒。

  問題出在狀態轉換的優先級。當處於雙引號字符串狀態時,遇到單引號不應該切換到字符常量狀態——因為單引號在字符串內部只是一個普通字符。

  他在狀態轉換函數裡加了一個判斷:只有在"普通代碼"狀態下,引號才能觸發狀態切換。在"雙引號字符串"狀態下,只有雙引號(非轉義的)能觸發閉合,單引號當作普通字符處理。

  改完重跑。八個用例全部通過。

  趙強沒有立刻喊"過了"——不像分號檢測那次,整層樓都聽見他叫。他只是靠在椅背上,長出了一口氣,然後把八個用例的輸入輸出在紙上一字一句核對了一遍。

  林遠注意到了這個變化。半年前的趙強過了測試會跳起來喊。現在的趙強過了測試會安靜地覆核。不是不興奮了,是興奮的方式變了——從外放變成內斂。

  "什麼感覺?"林遠問。

  "比括號匹配有意思。"趙強說,"括號匹配是進一個出一個的對稱結構,字符串閉合是'進去就得出來'的追蹤問題。看起來簡單,但轉義字符、續行、注釋里的假引號——全是陷阱。"

  "那個bug——單引號被誤判——你覺得根本原因是什麼?"

  "狀態轉換的上下文約束。"趙強說,"不同狀態下規則不一樣。在普通代碼下遇到任何引號都要處理,但在字符串狀態下只有匹配的引號才有意義。"他頓了一下,"跟分號檢測一個道理——不同狀態不同規則。但prev_state和作用域樹的parent指針其實是一個思路:記錄'從哪來的',才能知道'往哪去'。"

  林遠點了一下頭,沒有多說。

  他注意到一個變化——趙強解決這個問題的過程中,沒有問過他一次。從分析到畫圖到寫代碼到修bug,全程獨立。

  分號檢測的時候需要教狀態機概念,變量檢測的時候需要點一個key設計的細節。但字符串閉合——一個字都沒問。

  趙強已經開始學會把不同的知識串聯起來,自己解決複雜問題了。

  下午,食堂門口貼了一張通知——"關於做好解除封閉管理準備工作的通知"。

  趙強拎著饅頭回來:"看見沒?"

  "看見了。"

  "做好準備工作——就是馬上要解除了唄?"

  "等最後一天數據確認。30號還是零的話,下午發正式通知。"

  趙強咬著饅頭,有點恍惚:"三十七天了。你還記得剛封校那會兒嗎?超市泡麵被搶光了,你囤了二十包。"

  "記得。"

  "那時候你說最多封兩個月。結果三十七天差不多了。"

  "我說的是'大概率不會超過兩個月'。"

  "行行行。那解封之後你第一件事幹什麼?"

  "回家。"

  "第二件呢?"

  "去騰迅。"

  "第三件呢?"

  "吃涮羊肉。蘇明遠說了請客的。"

  蘇明遠晚上回來帶了個消息。

  "WatchGuard海淀那個客戶——上次說沒預算的——簽了。二十個節點,年費制。"


  "案例簡報起效果了?"

  "他們技術總監原話——'你們是我們看過的第一家能提供完整行業案例的安全公司'。"蘇明遠坐下來,"案例簡報這個思路是對的。客戶不在乎技術參數有多漂亮,他在乎的是'跟我一樣的人用了效果怎麼樣'。"

  "其他行業加快。"

  "已經在做了。金融和教育好了,醫療正在寫。"

  窗外傳來遠處的聲音——有人在唱歌。不知道哪個寢室,隔著窗戶聽不真切,但旋律很熟悉。

  趙強側耳聽了一會兒:"《七里香》?"

  "嗯。"

  "這歌都火到別的樓層了。"

  林遠沒說話。六首歌的進度一直在推——七里香和江南錄完音,童話demo錄完,一千年以後和歐若拉編曲定稿,寧夏還在排期。封校把這些事拖慢了,但沒拖停。

  遠處的歌聲漸漸停了。校園重新安靜下來。

  十一點,燈滅了。

  趙強翻到筆記本第五條——注釋閉合。

  "這個和字符串閉合有點像。"他自言自語,"都是追蹤開-關狀態。區別是注釋的標記是/*和*/,兩個字符不是單個。而且注釋裡面沒有轉義。"

  他想了想:"難點在//和/*的區分。單行注釋不需要閉合,多行注釋才需要。遇到/之後要看下一個字符是什麼。"

  "明天能搞定嗎?"

  "能。"

  安靜了一會兒。

  "你知道這五條做完意味著什麼嗎?"趙強說,"墨鋒的語法檢查引擎,基礎框架搭完了。分號、變量、括號、字符串、注釋——這五條覆蓋了C語言最常見的錯誤類型。後面十三條都是在框架上擴展,引擎核心不用再改。"

  又安靜了一會兒。

  "說真的,"趙強的聲音變輕了,"剛來北達的時候你讓我寫狀態機,我其實不太懂。現在回頭看——狀態機、作用域樹、棧結構、引號狀態追蹤——每個東西單獨拿出來都要想很久,但現在能自己把這些組合起來解決新問題了。"

  安靜了一會兒。

  "五條做完,墨鋒的語法檢查引擎基礎框架就搭完了。"趙強說,"後面十三條都是在框架上擴展,核心不用再改。"

  又安靜了一會兒。

  "明天搞注釋閉合,後天解封。"趙強說。

  "嗯。"

  趙強笑了。

  封校第三十七天。SARS連續第二天零新增。趙強字符串閉合全程獨立完成——五個狀態、八個邊界用例、一次bug修復。WatchGuard案例簡報產出新訂單。校園門口貼了解封準備通知。

  明天,門會打開。


關閉
Δ