今天晚上羣裏面一名叫“成都-go-戒炸雞”的羣友提出了他最近面試的一些面試題,面試題內容個人覺得非常典型、也非常有代表性和針對性,故拿出來與大家分享一下,也感謝他的分享。成都-go-戒炸雞說:
“今天面試,我沒答出來的有 redis 持久化機制,redis 銷燬方式機制,mq 實現原理,c++虛函數,hash 衝突的解決,memcached 一致性哈希,socket 函數 select 的缺陷,epoll 模型,同步互斥,異步非阻塞,回調的概念,innodb 索引原理,單向圖最短路徑,動態規劃算法。”
爲了避免問題有歧義,面試題略有修改。
思路分析
從面試題的內容可以看出,這是一個後臺開發的職位。
除了關於 c++ 虛函數這個問題以外,其他的大多數問題都與哪種編程語言關係不大,大多數是原理性和基礎性的問題,少數是工作經驗問題,筆者試着給大家分析。
語言基礎
C++ 虛函數這是面試初、中級 C ++ 職位一個概率 95%以上的面試題。一般有以下幾種問法:
-
在有繼承關係的父子類中,構建和析構一個子類對象時,父子構造函數和析構函數的執行順序分別是怎樣的?
-
在有繼承關係的類體系中,父類的構造函數和析構函數一定要申明爲 virtual 嗎?如果不申明爲 virtual 會怎樣?
-
什麼是 C++ 多態? C++ 多態的實現原理是什麼?
-
什麼是虛函數?虛函數的實現原理是什麼?
-
什麼是虛表?虛表的內存結構佈局如何?虛表的第一項(或第二項)是什麼?
-
菱形繼承(類 D 同時繼承 B 和 C,B 和 C 又繼承自 A )體系下,虛表在各個類中的佈局如何?如果類 B 和類 C 同時有一個成員變了 m,m 如何在 D 對象的內存地址上分佈的?是否會相互覆蓋?
算法與數據結構基礎
說到算法和數據結構,對於社招人士和對於應屆生一般是不一樣的,對於大的互聯網公司和一般的小的企業也是不一樣的。下面根據我當面試官面試別人和找工作被別人面試經驗來談一談。
先說考察的內容,除了一些特殊的崗位,常見的算法和數據結構面試問題有如下:
- 排序(常考的排序按頻率考排序爲:快速排序 > 冒泡排序 > 歸併排序 > 桶排序)
一般對於對算法基礎有要求的公司,如果你是應屆生或者工作經驗在一至三年內,以上算法如果寫不出來,給面試官的影響會非常不好,甚至直接被 pass 掉。對於工作三年以上的社會人士,如果寫不出來,但是能分析出其算法複雜度、最好和最壞的情況下的複雜度,說出算法大致原理,在多數面試官面前也可以過的。注意,如果你是學生,寫不出來或者寫的不對,基本上面試過不了。
-
二分查找
二分查找的算法儘量要求寫出來。當然,大多數面試官並不會直接問你二分查找,而是結合具體的場景,例如如何求一個數的平方根,這個時候你要能想到是二分查找。我在 2017 年年底,面試 agora 時,面試官問了一個問題:如何從所有很多的 ip 地址中快速找個某個 ip 地址。
-
鏈表
無論是應屆生還是工作年限不長的社會人士,璉表常見的操作一定要熟練寫出來,如鏈表的查找、定位、反轉、連接等等。還有一些經典的問題也經常被問到,如兩個鏈表如何判斷有環(我在 2017 年面試餓了麼二面、上海黃金交易所一面被問過)。鏈表的問題一般不難,但是鏈表的問題存在非常多的“坑”,如很多人不注意邊界檢查、空鏈表、返回一個鏈表的函數應該返回鏈表的頭指針等等。
-
隊列與棧
對於應屆生來說一般這一類問的比較少,但是對於社會人士尤其是中高級崗位開發,會結合相關的問題問的比較多,例如讓面試者利用隊列寫一個多線程下的生產者和消費者程序,全面考察的多線程的資源同步與競態問題(下文介紹多線程面試題時詳細地介紹)。
棧一般對於基礎要求高的面試,會結合函數調用實現來問。即函數如何實現的,包括函數的調用的幾種常見調用方式、參數的入棧順序、內存棧在地址從高向低擴展、棧幀指針和棧頂指針的位置、函數內局部變量在棧中的內存分佈、函數調用結束後,調用者和被調用者誰和如何清理棧等等。某年面試京東一基礎部門,面試官讓寫從 0 加到 100 這樣一個求和算法,然後寫其彙編代碼。
-
哈希表
哈希表是考察最多的數據結構之一。常見的問題有哈希衝突的檢測、讓面試者寫一個哈希插入函數等等。基本上一場面試下來不考察紅黑樹基本上就會問哈希表,而且問題可淺可深。我印象比較深刻的是,當年面試百度廣告推薦部門時,二面問的一些關於哈希表的問題。當時面試官時先問的鏈表,接着問的哈希衝突的解決方案,後來讓寫一個哈希插入算法,這裏需要注意的是,你的算法中插入的元素一定要是通用元素,所以對於 C++ 或者 Java 語言,一定要使用模板這一類參數作爲哈希插入算法的對象。然後,就是哈希表中多個元素衝突時,某個位置的元素使用鏈表往後穿成一串的方案。最終考察 linux 下 malloc (下面的 ptmalloc ) 函數在頻繁調用造成的內存碎片問題,以及開源方案解決方案 tcmalloc 和 jemalloc。總體下來,面試官是一步步引導你深入。(有興趣的讀者可以自行搜索,網上有很多相關資料)
-
樹
面試高頻的樹是紅黑樹,也有一部分是 B 樹( B+樹)。
紅黑樹一般的問的深淺不一,大多數面試官只要能說出紅黑樹的概念、左旋右旋的方式、分析出查找和插入的平均算法複雜度和最好最壞時的算法複雜度,並不要寫面試者寫出具體代碼實現。一般 C++ 面試問 stl 的 map,java 面試問 TreeMap 基本上就等於開始問你紅黑樹了,要有心裏準備。筆者曾經面試愛奇藝被問過紅黑樹。
B 樹一般不會直接問,問的最多的形式是通過問 MySQL 索引實現原理(數據庫知識點將在下文中討論)。筆者面試騰訊看點部門二面被問到過。
-
圖
圖的問題就我個人面試從來沒遇到過,不過據我某位哥哥所說,他在進三星電子之前有一道面試題就是深度優先和廣度優先問題。
-
其他的一些算法
如 A*尋路、霍夫曼編碼也偶爾會在某一個領域的公司的面試中被問到,如寶開(《植物大戰殭屍》的母公司, 在上海人民廣場附近有分公司)。
編碼基本功
還有一類面試題不好分類,筆者姑且將其當作是考察編碼基本功,這類問題既可以考察算法也可以考察你寫代碼基本素養,這些素養不僅包括編碼風格、計算機英語水平、調試能力等,還包括你對細節的掌握和易錯點理解,如有意識地對邊界條件的檢查和非法值的過濾。請讀者看以下的代碼執行結果是什麼?(筆者 2011 年去北京中關村的鼎普面試的問題)
for(char i = 0; i 256; ++i)
{
printf("%d\n", i);
}
下面再列舉幾個常見的編碼題:
-
實現一個 memmov 函數
這個題目考查點在於 memmov 函數與 memcpy 函數的區別,這兩者對於源地址與目標地址內存有重疊的這一情況的處理方式是不一樣的。
-
實現 strcpy 或 strncpy 函數
這個函數寫出來沒啥難度,但是除了邊界條件需要檢查以外,還有一個容易被忽視的地方即其返回值一定要是目標內存地址,以支持所謂的鏈式拷貝。即:
strcpy(dest3, strcpy(dest2, strcpy(dest1, src1)));
-
實現 atoi 函數
這個函數的簽名如下:
int atoi(const char* p);
容易疏忽的地方有如下幾點:
-
小數點問題,如數字 0.123 和.123 都是合法的;
-
正負號問題,如+123 和-123 ;
-
考慮如何識別第一個非法字符問題,如 123Z89,則應轉換成應該 123。
我在面試掌門科技(無線萬能鑰匙那一家)就遇到過這樣的問題。
多線程開發基礎
現如今的多核 CPU 早已經是司空見慣,而多線程編程早已經是“飛入尋常百姓家”。對於大多數桌面應用(與 Web 開發相對),尤其是像後臺開發這樣的崗位,且面試者是社會人員(有一定的工作經驗),如果面試者不熟悉多線程編程,那麼一般會被直接 pass 掉。
這裏說的“熟悉多線程編程”到底熟悉到什麼程度呢?一般包括:知道何種場合下需要新建新的線程、線程如何創建和等待、線程與進程的關係、線程局部存儲( TLS 或者叫 thread local )、多線程訪問資源產生競態的原因和解決方案等等、熟練使用所在操作系統平臺提供的線程同步的各種原語。
對於 C++ 開發者,你需要:
-
對於 Windows 開發者,你需要熟練使用 Interlock 系列函數、CriticalSection、Event、Mutex、Semphore 等 API 函數和兩個重要的函數 WaitForSingleObject、WaitForMultipleObjects。
-
對於 linux 開發者,你需要熟練使用 mutex、semphore、condition_variable、read-write-lock 等操作系統 API。
對於 Java,你需要熟悉使用 synchronized 關鍵字、CountDownLatch、CyclicBarrier、Semaphore 以及 java.util.concurrent 等包中的大多數線程同步對象。
數據庫
數據庫知識一般在大的互聯網企業對應屆生不做硬性要求,對於小的互聯網企業或社會人士一般有一定的要求。其要求一般包括:
-
熟悉基本 SQL 操作
包括增刪改查( insert、delete、update、select 語句),排序 order,條件查詢( where 子語句),限制查詢結果數量( LIMIT 語句)等
-
稍微高級一點的 SQL 操作(如 Group by,in,join,left join,多表聯合查詢,別名的使用,select 子語句等)
-
索引的概念、索引的原理、索引的創建技巧
-
數據庫本身的操作,建庫建表,數據的導入導出
-
數據庫用戶權限控制(權限機制)
-
MySQL 的兩種數據庫引擎的區別
-
SQL 優化技巧
網絡編程
網絡編程這一塊,對於應屆生或者初級崗位一般只會問一些基礎網絡通信原理(如三次握手和四次揮手)的 socket 基礎 API 的使用,客戶端與服務器端網絡通信的流程(回答 [客戶端創建 socket -> 連接 server ->收發數據;服務器端創建 socket -> 綁定 ip 和端口號 -> 啓動偵聽 ->接受客戶端連接 ->與客戶端通信收發數據] 即可)、TCP 與 UDP 的區別等等。
對於工作經驗三年以內的社會人士或者一些中級面試者一般會問一些稍微重難點問題,如 select 函數的用法,非阻塞 connect 函數的寫法,epoll 的水平和邊緣模式、阻塞 socket 與非阻塞 socket 的區別、send/recv 函數的返回值情形、reuse_addr 選項等等。Windows 平臺可能還會問 WSAEventSelect 和 WSAAsyncSelect 函數的用法、完成端口( IOCP 模型)。
對於三年以上尤其是“號稱”自己設計過服務器、看過開源網絡通信庫代碼的面試者,面試官一般會深入問一些問題,這類問題要麼是實際項目中常見的難題或者網絡通信細節,根據我的經驗,一般有這樣一些問題:
-
nagle 算法;
-
keepalive 選項;
-
Linger 選項;
-
對於某一端出現大量 CLOSE_WAIT 或者 TIME_WAIT 如何解決;
-
通訊協議如何設計或如何解決數據包的粘包與分片問題;
-
心跳機制如何設計;(可能不會直接問問題本身,如問如何檢查死鏈)
-
斷線重連機制如何設計;
-
對 IO Multiplexing 技術的理解;
-
收發數據包正確的方式,收發緩衝區如何設計;
-
優雅關閉;
-
定時器如何設計;
-
epoll 的實現原理。
舉個例子,讓讀者感受一下,筆者曾去 BiliBili 被問過這樣一個問題:如果 A 機器與 B 機器網絡 connect 成功後從未互發過數據,此時其中一機器突然斷電,則另外一臺機器與斷電的機器之間的網絡連接處於哪種狀態?
內存數據庫技術
時下以 NoSql key-value 爲思想的內存數據庫大行其道,廣泛地用於各種後臺項目開發。所以熟悉一種或幾種內存數據庫程序已經是面試後臺開發的基本要求,而這當中以 redis 和 memcached 爲最典型代表,這裏以 redis 爲例。
-
第一層面一般是對 redis 的基礎用法的考察
如考察 redis 支持的基礎數據類型、redis 的數據持久化、事務等。
-
第二層面不僅考察 redis 的基礎用法,還會深入到 redis 源碼層面上,如 redis 的網絡通信模型、redis 各種數據結構的實現等等。
筆者以爲,無論是從找工作應付面試還是從提高技術的角度,redis 是一個非常值得學習的開源軟件,希望廣大讀者有意識地去了解、學習它。
項目經驗
除了社會招聘和一些小型的企業,一般的大型互聯網公司對應屆生不會做過多的項目經驗要求,而是希望他們算法與數據結構等基礎紮實、動手實踐能力強即可。對於一般的小公司,對於應屆生會要求其至少熟練使用一門編程語言以及相應的開發工具,號稱熟悉 linux C++ 開發的面試者,不熟悉 GDB 調試基本上不是真正的熟悉 linux C++ 開發;號稱熟悉彙編或者反彙編,不熟悉 IDA 或者 OllyDbg,基本上也是名不符實的;號稱熟悉 VC++ 開發,連 F8、F9、F10、F11、F12 等快捷鍵不熟悉也是難以經得住面試官的提問的;號稱熟悉 Java 開發的卻對 IDEA 或 eclipse 陌生,這也是說不過去的。
這裏給一些學歷不算好,學校不是非常有名,尤其是二本以下的廣大想進入 IT 行業的同學一個建議,在大學期間除了要學好計算機專業基礎知識以外,一定要熟練使用一門編程語言以及相應的開發工具。
關於項目經驗,許多面試者認爲一定要是自己參與的項目,其實也可以來源於你學習和閱讀他人源碼或開源軟件的源碼,如果你能理解並掌握這些開源軟件中的思想和技術,在面試的時候能夠與面試官侃侃而談,面試官也會非常滿意的。筆者的一個學弟前段時間告訴我,他看懂了我公衆號 [easyserverdev] 中《服務器開發基礎系列和進階》的文章後,成功拿到了網易的 offer,有興趣的讀者可以好好看一下。
很多同學可能糾結大學或者研究生期間要不要跟着導師做一些項目。當然,如果這些項目是課程要求,那麼你必須得參加;如果這些項目是可以選擇性的,尤其是一些僅僅拿着第三方的庫進行所謂的包裝和加工,那麼建議可以少參加一些。
思路總結
不知道通過我上面的技術分析,聰明的讀者是否已經明確本文開頭“成都-go-戒炸雞”同學提出的面試題中,哪些是技術面試重難點,哪些又是技術開發的重難點呢?
技術比重與薪資
這裏根據我自己招人的經驗來談一談技術水平與薪資,就上面的面試題來看:
-
第一層次:如果面試者能答出上面面試題中的 C++基礎問題和算法與數據結構題目(如 C++ 函數與 hash 衝突的解決、innodb 索引原理,單向圖最短路徑,動態規劃算法等),可以認爲面試者是一個合格的初、中級開發者,薪資範圍一般在 6 ~ 12k (注意:這裏以我所在的上海爲參考標準)。
-
第二層次:在第一層次基礎之上,如果面試者還能答出上述面試題中網絡編程相關的或者多線程相關的問題(如 socket 函數 select 的缺陷,epoll 模型,同步互斥,異步非阻塞,回調的概念等),可以認爲面試者是個基礎不錯的中級開發者,薪資範圍一般在 14 ~ 22k 之間。
-
第三層次:在前兩個層次之間,如果面試者還能回答出上述問題中關於 redis、memcached 和 mq 實現原理,說明面試者是一個有着不錯項目經驗並且對一些常用開源項目也有一定的理解,薪資可以給到 22k +。
總結
工資收入是每個人的祕密,一般不輕易對外人道也。這裏筆者冒天下之大不韙,只想說明一點——對於普通開發人員,提高薪資最好的捷徑就是提高自己的技術,無論是“面向搜索引擎編程”還是“面向工資編程”終將得不償失,聰明的你一定會深謀遠慮的。