機(jī)到C語言陷阱)
2019年秋天我坐在某高校機(jī)房里屏幕上是一道關(guān)于TCP狀態(tài)轉(zhuǎn)換的編程題右邊倒計(jì)時(shí)還剩四十分鐘。那是我第一次參加思科校園招聘軟件類筆試。說實(shí)話網(wǎng)上關(guān)于思科筆試的討論大多集中在“思科模擬器怎么用”“交換機(jī)怎么配”真正針對軟件類崗位的筆試細(xì)節(jié)少得可憐。我后來拿到了面試資格也陸續(xù)幫幾個(gè)學(xué)弟學(xué)妹梳理過這套筆試今天把能回憶起來的、能復(fù)現(xiàn)的、能避免的坑一次性寫清楚。如果你正在準(zhǔn)備思科或同類外企的軟件類校招筆試這篇應(yīng)該能幫你把復(fù)習(xí)范圍縮小不少。1. 思科軟件類筆試的宏觀印象不是純算法競賽1.1 筆試形式與時(shí)長機(jī)考、多模塊、時(shí)間緊思科2019年軟件類校招筆試采用在線機(jī)考全國同步題型分為客觀題和編程題兩大部分。我參加的那場時(shí)長是90分鐘題量大約在40道左右其中編程題兩道剩下全是選擇題和判斷題。90分鐘聽起來不算短但實(shí)際做題時(shí)你會發(fā)現(xiàn)選擇題里摻雜了大量網(wǎng)絡(luò)和Linux內(nèi)容不熟悉的話每道題都要磨很久編程題雖然只有兩道但第一道往往就有一定難度讀題加思考很容易花掉半小時(shí)。這里要提醒一個(gè)關(guān)鍵點(diǎn)思科的筆試系統(tǒng)是分模塊計(jì)時(shí)的還是統(tǒng)一計(jì)時(shí)各年份可能不一樣。2019年我遇到的是統(tǒng)一計(jì)時(shí)這意味著你完全可以跳過卡殼的選擇題先寫編程題但我當(dāng)時(shí)沒有經(jīng)驗(yàn)在選擇題上死磕了太久導(dǎo)致編程題時(shí)間有點(diǎn)緊張。后面我會具體說時(shí)間分配。1.2 題型結(jié)構(gòu)選擇題、判斷題、編程題的配比從當(dāng)年周圍同學(xué)反饋和我自己整理的信息來看客觀題覆蓋方向比較固定數(shù)據(jù)結(jié)構(gòu)和算法基礎(chǔ)大約占30%網(wǎng)絡(luò)基礎(chǔ)占25%Linux/操作系統(tǒng)占20%C/C語言細(xì)節(jié)占15%其他智力題、數(shù)學(xué)邏輯等占10%。編程題則基本落在字符串處理、數(shù)組操作、簡單的數(shù)據(jù)結(jié)構(gòu)應(yīng)用上偶爾會出現(xiàn)一道與網(wǎng)絡(luò)協(xié)議相關(guān)的模擬題。這個(gè)配比很有意思。思科畢竟是網(wǎng)絡(luò)設(shè)備公司即使招軟件工程師也默認(rèn)你對網(wǎng)絡(luò)協(xié)議有基本認(rèn)知。所以如果你只刷LeetCode不碰網(wǎng)絡(luò)知識很可能會在客觀題上吃虧。我認(rèn)識一個(gè)算法很強(qiáng)但網(wǎng)絡(luò)幾乎零基礎(chǔ)的哥們筆試后面試都沒進(jìn)就是網(wǎng)絡(luò)題錯(cuò)太多。2. 編程題的真實(shí)難度與解題策略LeetCode中等題足夠2.1 一道典型的字符串處理題拆解當(dāng)年編程題第一道大概是這樣的給定一個(gè)字符串要求按單詞翻轉(zhuǎn)但保留單詞內(nèi)部字符順序并處理多余空格。題目描述有點(diǎn)像LeetCode 151但多了一個(gè)約束——字符串可能包含首尾空格和連續(xù)空格要求輸出時(shí)保持一個(gè)空格分隔。這類題本質(zhì)上考察的是字符串分割與反轉(zhuǎn)不涉及復(fù)雜算法但特別考驗(yàn)邊界處理。我當(dāng)時(shí)第一版直接用了C的istringstream按空格分割然后反轉(zhuǎn)輸出。這能通過正常用例但忽視了“保持標(biāo)點(diǎn)符號與單詞一起翻轉(zhuǎn)”的細(xì)節(jié)。題目里并沒有說標(biāo)點(diǎn)要作為獨(dú)立token意味著“hello, world”翻轉(zhuǎn)后應(yīng)該是“world hello,”而不是“world, hello”。如果你用istringstream逗號會粘連在單詞上翻轉(zhuǎn)后位置不對測試點(diǎn)大概率掛一個(gè)。正確的做法是先按字符遍歷把單詞和標(biāo)點(diǎn)作為一個(gè)整體切分再逆序拼接到結(jié)果字符串。這里有個(gè)小經(jīng)驗(yàn)處理字符串題時(shí)先別急著調(diào)庫函數(shù)把題目要求的邊界條件寫在注釋里。比如“首尾空格去掉”“連續(xù)空格壓縮為一個(gè)”“標(biāo)點(diǎn)隨單詞整體移動(dòng)”寫完注釋再動(dòng)手寫代碼能少踩很多坑。2.2 出現(xiàn)過的網(wǎng)絡(luò)協(xié)議模擬題不只是背狀態(tài)碼第二道編程題我印象比較深是模擬TCP連接狀態(tài)遷移。題目給出了一個(gè)狀態(tài)轉(zhuǎn)移表要求根據(jù)輸入的事件序列輸出最終狀態(tài)狀態(tài)包括LISTEN、SYN_SENT、ESTABLISHED等等。這其實(shí)是一道“按規(guī)則模擬”的題難度不高但需要你把狀態(tài)表記清楚或者題目會給出狀態(tài)表讓你實(shí)現(xiàn)。這種題就是思科筆試的特色——它不考你DP、不考你圖論而是考你“能不能把現(xiàn)實(shí)協(xié)議邏輯準(zhǔn)確翻譯成代碼”。我當(dāng)時(shí)對TCP狀態(tài)機(jī)其實(shí)記憶模糊但題目貼心地把狀態(tài)轉(zhuǎn)移表打印在題目描述里所以我只需要建一個(gè)map把“當(dāng)前狀態(tài)事件”映射到“下一狀態(tài)”循環(huán)處理輸入序列就行。這種題的經(jīng)驗(yàn)是不要試圖在考場上推導(dǎo)狀態(tài)轉(zhuǎn)移直接用二維映射或者哈希表硬編碼代碼越直白越好。同時(shí)要注意輸入事件序列可能包含非法事件需要輸出error或忽略一定要看題目對異常輸入的定義。2.3 時(shí)間不夠時(shí)的保分順序編程題滿分往往占據(jù)筆試總分的40%到50%所以策略應(yīng)該是先快速瀏覽兩道編程題挑一道更簡單的先做。我那年第一道字符串題明顯比第二道狀態(tài)模擬題短所以我先做了第一道但狀態(tài)模擬題其實(shí)更簡單就是代碼長一些。如果你讀過題后發(fā)現(xiàn)兩道題都能做先做代碼量小的如果一道題基本沒思路立刻放棄把時(shí)間留給另一道。關(guān)于編譯器環(huán)境思科筆試系統(tǒng)支持C、C、Java和Python但我在實(shí)際考試時(shí)發(fā)現(xiàn)Python的輸入輸出速度有點(diǎn)慢——不是執(zhí)行慢而是IO模塊需要額外導(dǎo)入容易在調(diào)試時(shí)浪費(fèi)時(shí)間。建議用你平時(shí)最熟練的語言但如果平時(shí)用Python提前練習(xí)一下sys.stdin.read()的寫法別在筆試現(xiàn)場邊查邊寫。3. 基礎(chǔ)題里的“思科特色”網(wǎng)絡(luò)、Linux、C語言怎么考3.1 TCP/IP協(xié)議棧是選擇題大戶如果你搜索“思科筆試”相關(guān)熱詞會發(fā)現(xiàn)大量關(guān)于“思科交換機(jī)配置”“思科命令手冊”的結(jié)果這些大多是網(wǎng)絡(luò)工程崗的內(nèi)容。但軟件類筆試?yán)锞W(wǎng)絡(luò)知識不會考配置命令而是考協(xié)議原理和報(bào)文細(xì)節(jié)。我記得至少有五道題涉及TCP三次握手和四次揮手的狀態(tài)變化比如“SYN_SENT狀態(tài)是在什么時(shí)候進(jìn)入的”“TIME_WAIT持續(xù)多久”這類。還有ARP協(xié)議的作用、DNS解析流程、IP地址分類和CIDR計(jì)算這些都是送分題。這里給出一個(gè)備考建議不需要看CCNA那種厚書把《TCP/IP詳解卷一》前15章快速過一遍重點(diǎn)看TCP狀態(tài)機(jī)、IP分片、TCP擁塞控制的基本概念就足夠應(yīng)付選擇題了。我印象里有一道題問的是主機(jī)A向主機(jī)B發(fā)送一個(gè)TCP連接請求A收到SYNACK后如果A此時(shí)發(fā)送的ACK丟失B會處于什么狀態(tài)選項(xiàng)里有SYN_RCVD、ESTABLISHED等。這題考的就是對TCP狀態(tài)遷移的精細(xì)理解如果你只是背了“三次握手”四個(gè)字很難選對。這種題沒有捷徑必須把狀態(tài)遷移圖畫一遍。3.2 Linux命令與Shell的常見考點(diǎn)思科軟件類筆試對Linux的考察偏向基礎(chǔ)命令和進(jìn)程概念。常見選擇題包括查看進(jìn)程占用CPU最高的命令top/ps sort、改變文件權(quán)限的命令chmod、在文件里搜索關(guān)鍵字grep、查看端口占用netstat或ss。還有一個(gè)高頻考點(diǎn)管道操作符|和重定向的區(qū)別以及crontab表達(dá)式的基本格式。我備考時(shí)會故意在虛擬機(jī)里把常用命令敲一遍尤其是那些容易混淆的選項(xiàng)比如ls -l輸出里的文件類型標(biāo)識第一列第一位是-表示普通文件d表示目錄l表示符號鏈接。筆試有一道題給了一段ls -l輸出要求判斷哪個(gè)是符號鏈接。如果你只刷選擇題不實(shí)操這種題很容易想當(dāng)然。Shell部分不需要太深入但要知道簡單的for循環(huán)和變量賦值比如${var}和$var的區(qū)別以及單引號雙引號對變量解析的影響。有一年考了echo $HOME和echo $HOME的輸出差異答這類題的時(shí)候留意單引號不展開變量。3.3 指針與內(nèi)存管理的經(jīng)典陷阱C語言相關(guān)的客觀題幾乎年年都有指針。思科內(nèi)部大量底層代碼是C/C所以他們很在意程序員對內(nèi)存的理解。常見考法有sizeof一個(gè)指針和sizeof一個(gè)數(shù)組的區(qū)別、指針和數(shù)組下標(biāo)訪問的關(guān)系、char*和char[]在修改時(shí)的行為差異。我印象最深的一道題是char *p hello; char arr[] hello; p[0] H; arr[0] H;問哪一行會報(bào)錯(cuò)。答案是p[0] H因?yàn)樽址A看鎯υ谥蛔x區(qū)試圖修改會引發(fā)段錯(cuò)誤。而arr是在棧上初始化的數(shù)組可以直接修改。這類題如果沒實(shí)際跑過很容易憑直覺答錯(cuò)。還有內(nèi)存泄漏的判斷題分配了malloc沒有free會不會導(dǎo)致程序立刻崩潰答案是通常不會但會造成內(nèi)存泄漏。這種題考察的是對資源管理的敏感性。我建議備考時(shí)做一個(gè)小項(xiàng)目刻意寫一段有內(nèi)存泄漏的代碼并用valgrind檢測感受一下泄漏的表現(xiàn)。4. 面試官視角筆試評分在看什么以及如何避免零分卷4.1 代碼規(guī)范與注釋是否真的會影響分?jǐn)?shù)很多考生覺得筆試答題只要AC了就行但思科這類外企的筆試評分系統(tǒng)通常會綜合評估代碼風(fēng)格。我后來問過一位參與校招的工程師他說他們確實(shí)會人工查看部分編程題答卷重點(diǎn)是看變量命名、邏輯結(jié)構(gòu)、異常處理。如果一段代碼AC了但寫滿全局變量、函數(shù)之間靠共享狀態(tài)通信即使通過測試用例在人工評審環(huán)節(jié)也可能被扣印象分。我建議準(zhǔn)備筆試時(shí)養(yǎng)成習(xí)慣每個(gè)函數(shù)寫清注釋說明輸入輸出變量命名用有語義的單詞不要用a、b、tmp代碼縮進(jìn)統(tǒng)一。這些好習(xí)慣不需要刻意練習(xí)刷題時(shí)用規(guī)范方式寫自然就內(nèi)化了。有一個(gè)小技巧在答卷開頭寫一個(gè)簡短的算法思路注釋比如“使用雙指針遍歷數(shù)組時(shí)間復(fù)雜度O(n)”這不會占太多時(shí)間但能讓面試官快速理解你的思路即使部分測試用例沒過也更容易給你加分。4.2 邊界條件的遺漏是扣分重災(zāi)區(qū)無論是LeetCode還是筆試實(shí)戰(zhàn)邊界條件都是拉開差距的地方。我自己的經(jīng)驗(yàn)是寫完代碼后至少檢查四類邊界——空輸入、單元素輸入、全部相同元素、最大規(guī)模輸入。比如字符串題如果輸入是空字符串你的代碼是否會導(dǎo)致越界如果輸入只有空格反轉(zhuǎn)后是否輸出空如果輸入是“a”這種單字符頭尾指針是否會出現(xiàn)交錯(cuò)有一道我印象很深的選擇題給了一段實(shí)現(xiàn)字符串拷貝的函數(shù)問哪里可能導(dǎo)致緩沖區(qū)溢出。題目中的函數(shù)用strcpy而沒有檢查目標(biāo)緩沖區(qū)長度。很多人一眼看出問題但還有一種隱蔽的坑源字符串和目的字符串如果指向同一塊內(nèi)存strcpy的行為是未定義的。C語言內(nèi)置函數(shù)這類坑必須多看多練。編程題里如果你沒有處理邊界很可能一兩組極端用例直接讓代碼崩潰拿到部分分?jǐn)?shù)甚至零分。所以強(qiáng)烈建議在筆試系統(tǒng)里手動(dòng)輸入幾個(gè)邊界用例測試一下不要只依賴系統(tǒng)給的示例。4.3 實(shí)戰(zhàn)復(fù)盤一道多線程題的錯(cuò)誤示范思科筆試偶爾會出現(xiàn)操作系統(tǒng)相關(guān)的編程題要求用多線程模擬某個(gè)場景。2019年我所在考場有一道題是模擬生產(chǎn)者-消費(fèi)者問題使用C的std::thread和mutex實(shí)現(xiàn)。大部分考生平時(shí)用Python刷算法題對線程同步不熟這道題成了分水嶺。我當(dāng)時(shí)的錯(cuò)誤是在消費(fèi)者線程里用while循環(huán)忙等待沒有用條件變量。代碼運(yùn)行起來CPU占用飆升而且由于沒有正確加鎖偶爾還會死鎖。雖然題目本身不大復(fù)雜但因?yàn)檫@種錯(cuò)誤我沒有拿到滿分。后來復(fù)盤時(shí)我總結(jié)了多線程題的核心套路用互斥鎖保護(hù)共享隊(duì)列用條件變量通知生產(chǎn)者或消費(fèi)者不要在鎖內(nèi)做耗時(shí)操作。如果你對多線程編程不熟建議至少在筆試前手動(dòng)寫一遍“生產(chǎn)者-消費(fèi)者”模型編譯運(yùn)行通過。不要只背概念因?yàn)楣P試環(huán)境里你很少有機(jī)會在線調(diào)試寫一遍運(yùn)行過才能真正理解鎖的粒度和條件變量的使用。5. 從筆試到Offer備考時(shí)間線與資源推薦5.1 倒計(jì)時(shí)四周的復(fù)習(xí)節(jié)奏如果你接到思科筆試通知距離考試通常還有一到兩周很少超過一個(gè)月。我建議把時(shí)間切成三段第一周主攻客觀題基礎(chǔ)選擇題涉及的知識點(diǎn)很零散每天花兩小時(shí)過網(wǎng)絡(luò)和Linux再花一小時(shí)刷C語言細(xì)節(jié)題。第二周重點(diǎn)刷編程題目標(biāo)是每天三道LeetCode中等題優(yōu)先覆蓋字符串、數(shù)組、哈希表、鏈表這些是思科筆試高頻范圍。第三周模擬筆試用牛客網(wǎng)或賽碼網(wǎng)的往年題嚴(yán)格按90分鐘限時(shí)練習(xí)訓(xùn)練時(shí)間分配。記住模擬筆試比單題刷題重要得多。我自己第一次模擬時(shí)完全沒時(shí)間做完練了三次之后才找到節(jié)奏。很多同學(xué)最后掛在時(shí)間不夠上其實(shí)不是不會而是不熟悉機(jī)考環(huán)境。5.2 親測好用的刷題平臺和書單??途W(wǎng)有大量名企校招真題尤其是客觀題部分很全可以直接搜“思科”篩選歷史題目。雖然2019年的題目不一定完整但題型和考點(diǎn)參考價(jià)值很大。LeetCode編程題準(zhǔn)備首選建議按照“字符串、數(shù)組、鏈表、棧與隊(duì)列、哈希表”的順序刷這些是思科筆試編程題最容易出現(xiàn)的知識點(diǎn)。不用碰困難題中等難度足夠?!冻绦騿T面試金典》里面有不少外企面試風(fēng)格的題目能幫你適應(yīng)英文題目和近似筆試的白板編碼感覺?!禩CP/IP詳解 卷一》不是讓你全部讀完重點(diǎn)看第13章到第18章覆蓋TCP狀態(tài)機(jī)、超時(shí)重傳、擁塞控制應(yīng)對網(wǎng)絡(luò)選擇題綽綽有余?!渡钊肜斫庥?jì)算機(jī)系統(tǒng)》第8章到第10章異常控制流、虛擬內(nèi)存、系統(tǒng)調(diào)用和I/O這部分內(nèi)容在操作系統(tǒng)選擇題里經(jīng)常出現(xiàn)。5.3 關(guān)于思科校招的幾點(diǎn)冷知識思科筆試成績通常不會當(dāng)場公布一般一周內(nèi)發(fā)面試通知。如果你遲遲沒收到可以禮貌發(fā)郵件咨詢HR但不要頻繁催促。筆試通過后面試流程通常有兩輪到三輪其中會有一輪英文自我介紹或英文問答。平時(shí)可以準(zhǔn)備一個(gè)兩分鐘左右的英文自我介紹技術(shù)問題用中文回答就行。思科某些軟件崗位會額外加一輪性格測試或邏輯測試時(shí)間不長沒有標(biāo)準(zhǔn)答案按照自己真實(shí)想法回答比較好前后矛盾反而容易出問題。筆試機(jī)房可能提供草稿紙但不一定允許帶計(jì)算器。涉及IP地址計(jì)算的選擇題建議熟練掌握常見的2的冪次和子網(wǎng)掩碼換算現(xiàn)場用筆算也很快。6. 我踩過的坑希望你閉著眼睛避開最后寫幾條只有真正考過才會懂的體會沒有邏輯順序都是血淚教訓(xùn)。第一不要因?yàn)橐坏肋x擇題卡住就慌。思科筆試的選擇題每題分值不大但浪費(fèi)時(shí)間會連鎖影響編程題。我當(dāng)年就是在一道關(guān)于TCP擁塞控制的題上糾結(jié)了太久那道題考的是“TCP Reno擁塞避免階段的閾值變化”選項(xiàng)很繞其實(shí)只要掌握慢啟動(dòng)、擁塞避免、快重傳、快恢復(fù)的基本流程就能排除兩個(gè)選項(xiàng)。我當(dāng)時(shí)憑模糊記憶猜了一個(gè)結(jié)果后面編程題差點(diǎn)來不及?,F(xiàn)在想想不如直接標(biāo)記跳過先把編程題做完。第二編程題如果實(shí)在解不出來一定把空函數(shù)和思路注釋寫上。即使不能通過測試用例人工評審也可能給步驟分。我親眼見過一個(gè)同學(xué)編程題提交的代碼編譯不過但注釋里寫清了“我準(zhǔn)備用動(dòng)態(tài)規(guī)劃定義dp[i]為到第i個(gè)字符為止的最長無重復(fù)子串長度”這被面試官注意到了后續(xù)面試時(shí)還專門問了他對動(dòng)態(tài)規(guī)劃的理解。雖然他沒進(jìn)但至少說明注釋不是白寫的。第三筆試前檢查網(wǎng)絡(luò)環(huán)境。在線筆試最怕斷網(wǎng)我有個(gè)學(xué)弟考到一半家里路由器重啟答完題上傳失敗HR根本不接受重考申請。雖然這是很小的概率但建議提前確認(rèn)網(wǎng)絡(luò)穩(wěn)定、關(guān)閉所有非必要軟件尤其是那些會自動(dòng)彈窗的即時(shí)通訊工具。第四關(guān)于“思科模擬器”“思科交換機(jī)配置”這些熱詞。你是去考軟件類崗位不是去考證照不用花時(shí)間去學(xué)PT模擬器的命令行操作。但如果你能理解交換機(jī)、路由器的工作原理對選擇題里的網(wǎng)絡(luò)部分會有幫助特別是VLAN劃分、ARP表項(xiàng)學(xué)習(xí)這些基礎(chǔ)概念。所以業(yè)余時(shí)間可以看幾集網(wǎng)絡(luò)基礎(chǔ)網(wǎng)課把二層交換和三層路由的模型建立起來但真的不用去背配置命令。第五心態(tài)上不要把筆試當(dāng)成“一次定生死”。思科校招筆試雖然重要但它只是篩選環(huán)節(jié)之一很多進(jìn)面試的同學(xué)筆試成績并不是頂尖。更重要的是通過這場筆試你會發(fā)現(xiàn)自己在網(wǎng)絡(luò)基礎(chǔ)、C語言細(xì)節(jié)、時(shí)間管理上的短板。哪怕這次沒通過把這些短板補(bǔ)齊對后續(xù)其他公司的筆試同樣有效。以上就是我能回憶起的2019年思科軟件類筆試的全貌。準(zhǔn)備過程沒有捷徑但方向?qū)α丝梢允∠麓罅繒r(shí)間。如果你按照文章里的模塊去復(fù)習(xí)至少不會在考場上遇到完全陌生的東西。祝每一位認(rèn)真準(zhǔn)備的求職者都能拿到心儀的面試通知。