日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

超全面的后端开发C/C++面经整理分享含详细参考答案 包括简历分享

發布時間:2023/12/10 c/c++ 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 超全面的后端开发C/C++面经整理分享含详细参考答案 包括简历分享 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文為博主2020年秋招提前批的c/c++后端開發面經整理,包括C/C++語言基礎,計網,數據庫,linux,操作系統,場景題,智力題和hr常問題。面試問題來自前人的工作和博主面試時遇到的值得記錄的問題,其中面試題答案多為博主自行解答(并且面試的時候也基本是這樣回答的),回答中都附上了參考資料的鏈接,全文共七萬余字,僅供大家作為面試準備材料的參考。

希望大家在準備面試的時候都能建立一個屬于自己的題庫,自己從網上尋找問題,然后自己進行解答,并且記錄下來,這樣在面試的時候就能行云流水般的回答上來。因此我把這份面經的源文件 markdown格式分享給大家:網盤鏈接
提取碼:229s

希望大家在這份面經上添磚加瓦或從頭開始,建一個屬于自己的面試題庫!

附上博主2020秋招進展:


TP-LINK提前批 軟開深圳:一面二面三面hr面 offer

shopee 后端深圳:筆試一面二面hr面 offer

快手提前批 客戶端 深圳:一面二面三面hr面 offer

騰訊提前批 c++ teg 深圳:一面二面hr面 offer

百度提前批 c++ 北京:一面二面三面+hr面測評 offer

阿里C++ 北京:筆試一面二面三面交叉面一面二面三面hr面 offer


字節跳動提前批 后端深圳:一面二面 掛

招銀網絡提前批 軟開深圳:一面二面三面 掛

京東提前批 后端 京東零售:簡歷 掛

猿輔導 服務端:筆試 掛


京東正式批 c++:筆試一面二面 放棄

網易互娛 游戲研發廣州: 筆試 放棄

嗶哩嗶哩 后端上海:筆試 放棄

虎牙提前批 后端廣州:放棄

拼多多提前批 后端:筆試 放棄


目錄

  • 寫在前面
        • 關于實習
        • 關于簡歷
        • 關于投遞
        • 關于面試準備
  • 1. 語言基礎 (C/C++)
        • (0) 指針和引用的區別
        • (1)在函數參數傳遞的時候,什么時候使用指針,什么時候使用引用?
        • (2) 堆和棧有什么區別
        • (3)堆快一點還是棧快一點?(字節提前批一面)
        • (4) new和delete是如何實現的,new 與 malloc的異同處
        • (5)既然有了malloc/free,C++中為什么還需要new/delete呢?
        • (6) C和C\+\+的區別
        • (7)delete和delete\[\]的區別
        • (8) C++、Java的聯系與區別,包括語言特性、垃圾回收、應用場景等(java的垃圾回收機制)
        • (9)C++和python的區別
        • (10) Struct和class的區別
        • (11) define 和const的聯系與區別(編譯階段、安全性、內存占用等)
        • (12) 在C\+\+中const的用法(定義,用途)
        • (13) C++中的static用法和意義
        • (14) 計算下面幾個類的大小:
        • (15) C++的STL介紹(這個系列也很重要,建議侯捷老師的這方面的書籍與視頻),其中包括內存管理allocator,函數,實現機理,多線程實現等
        • (16) STL源碼中的hash表的實現
        • (17)解決哈希沖突的方式?
        • (18) STL中unordered_map和map的區別
        • (19) STL中vector的實現
        • (20) vector使用的注意點及其原因,頻繁對vector調用push_back()對性能的影響和原因。
        • (21)C++中vector和list的區別
        • (22) C++中的重載和重寫的區別:
        • (23) C ++內存管理(熱門問題)
        • (24) 介紹面向對象的三大特性,并且舉例說明每一個。
        • (25) 多態的實現(和下個問題一起回答)
        • (26) C++虛函數相關(虛函數表,虛函數指針),虛函數的實現原理(熱門,重要)
        • (27) 實現編譯器處理虛函數表應該如何處理
        • (28) 基類的析構函數一般寫成虛函數的原因
        • (29) 構造函數為什么一般不定義為虛函數
        • (30) 構造函數或者析構函數中調用虛函數會怎樣
        • (31) 純虛函數
        • (32) 靜態綁定和動態綁定的介紹
        • (33) 深拷貝和淺拷貝的區別(舉例說明深拷貝的安全性)
        • (34) 對象復用的了解,零拷貝的了解
        • (35) 介紹C++所有的構造函數
        • (36) 什么情況下會調用拷貝構造函數(三種情況)
        • (37) 結構體內存對齊方式和為什么要進行內存對齊?
        • (38) 內存泄露的定義,如何檢測與避免?
        • (39) C++的智能指針有哪些
        • (40) 調試程序的方法
        • (41) 遇到coredump要怎么調試
        • (42) inline關鍵字說一下 和宏定義有什么區別
        • (43) 模板的用法與適用場景 實現原理
        • (44) 成員初始化列表的概念,為什么用成員初始化列表會快一些(性能優勢)?
        • (45) 用過C11嗎,知道C11新特性嗎?(有面試官建議熟悉C11)
        • (46) C++的調用慣例(簡單一點C++函數調用的壓棧過程)
        • (47) C++的四種強制轉換
        • (48)string的底層實現
        • (49)一個函數或者可執行文件的生成過程或者編譯過程是怎樣的
        • (50)set,map和vector的插入復雜度
        • (51)定義和聲明的區別
        • (52)typdef和define區別
        • (53)被free回收的內存是立即返還給操作系統嗎?為什么
        • (54)引用作為函數參數以及返回值的好處
        • (55)友元函數和友元類
        • (56) 說一下volatile關鍵字的作用
        • (57) STL中的sort()算法是用什么實現的,stable_sort()呢
        • (58)vector會迭代器失效嗎?什么情況下會迭代器失效?
        • (58)為什么C++沒有實現垃圾回收?
  • 2. 計網相關
        • (1) 建立TCP服務器的各個系統調用
        • (2) 繼上一題,說明socket網絡編程有哪些系統調用?其中close是一次就能直接關閉的嗎,半關閉狀態是怎么產生的?
        • (3) 對路由協議的了解與介紹。內部網關協議IGP包括RIP,OSPF,和外部網關協議EGP和BGP.
        • (4) UDP如何實現可靠傳輸
        • (5) TCP和UDP的區別
        • (6) TCP和UDP相關的協議與端口號
        • (7) TCP(UDP,IP)等首部的認識(http請求報文構成)
        • (8) 網頁解析的過程與實現方法
        • (9) 在瀏覽器中輸入URL后執行的全部過程(如www.baidu.com)重點、熱門問題
        • (10) 網絡層分片的原因與具體實現
        • (11) TCP的三次握手與四次揮手的詳細介紹(TCP連接建立與斷開是熱門問題)
        • (12) TCP握手以及每一次握手客戶端和服務器端處于哪個狀態
        • (13) 為什么使用三次握手,兩次握手可不可以?(重點,熱門問題)
        • (14) TIME_WAIT的意義(為什么要等于2MSL)
        • (15) 超時重傳機制(不太高頻)
        • (16) TCP怎么保證可靠性?
        • (17) 流量控制的介紹,采用滑動窗口會有什么問題(死鎖可能,糊涂窗口綜合征)?
        • (18) tcp滑動窗口協議
        • (19) 擁塞控制和流量控制的區別
        • (20) TCP擁塞控制,算法名字?(極其重要)
        • (21) http協議與TCP的區別與聯系
        • (22) http/1.0和http/1.1的區別
        • (23) http的請求方法有哪些?get和post的區別。
        • (24) http的狀態碼 403 201等等是什么意思
        • (25) http和https的區別,由http升級為https需要做哪些操作
        • (26) https的具體實現,怎么確保安全性
        • (27) TCP三次握手時的第一次的seq序號是怎樣產生的 (字節提前批)
        • (28) 一個機器能夠使用的端口號上限是多少,為什么?可以改變嗎?那如果想要用的端口超過這個限制怎么辦?
        • (29) 對稱密碼和非對稱密碼體系
        • (30) 數字證書的了解(高頻)
        • (31) 服務器出現大量close_wait的連接的原因以及解決方法
        • (32) 消息摘要算法列舉一下,介紹MD5算法,為什么MD5是不可逆的,有什么辦法可以加強消息摘要算法的安全性讓它不那么容易被破解呢?(百度安全一面)
        • (33) 單條記錄高并發訪問的優化
        • (34) 介紹一下ping的過程,分別用到了哪些協議 (百度安全等)
        • (35) TCP/IP的粘包與避免介紹一下
        • (36) 說一下TCP的封包和拆包
        • (37) 一個ip配置多個域名,靠什么識別?
        • (38) 服務器攻擊(DDos攻擊)
        • (39)DNS的工作過程和原理
        • (41)OSA七層協議和五層協議,分別有哪些
        • (42)IP尋址和MAC尋址有什么不同,怎么實現的
  • 3. 數據庫
        • (1) 關系型和非關系型數據庫的區別(低頻)
        • (2) 什么是非關系型數據庫(低頻)
        • (3) 說一下 MySQL 執行一條查詢語句的內部執行過程?
        • (4) 數據庫的索引類型
        • (5) 說一下事務是怎么實現的
        • (6) MySQL怎么建立索引,怎么建立主鍵索引,怎么刪除索引?
        • (7) 索引的優缺點,什么時候使用索引,什么時候不能使用索引(重點)
        • (8) 索引的底層實現(重點)
        • (9) B樹和B+樹的區別(重點)
        • (10) 索引最左前綴/最左匹配
        • (11) Mysql的優化(高頻,索引優化,性能優化)
        • (12) MYSQL數據庫引擎介紹,innodb和myisam的特點與區別
        • (13) 數據庫中事務的ACID(四大特性都要能夠舉例說明,理解透徹,比如原子性和一致性的關聯,隔離性不好會出現的問題)
        • (14)什么是臟讀,不可重復讀和幻讀?
        • (15) 數據庫的隔離級別,mysql和Oracle的隔離級別分別是什么(重點)
        • (16) 數據庫連接池的作用
        • (17) Mysql的表空間方式,各自特點
        • (18) 分布式事務
        • (19) 數據庫的范式
        • (20) 數據的鎖的種類,加鎖的方式
        • (21) 什么是共享鎖和排他鎖
        • (22) 分庫分表的理解和簡介
        • (23)
        • (24)數據庫高并發的解決方案
        • (25)樂觀鎖與悲觀鎖解釋一下
        • (26)樂觀鎖與悲觀鎖是怎么實現的
        • (27)對數據庫目前最新技術有什么了解嗎
  • 4. Linux
        • (1) Linux的I/O模型介紹以及同步異步阻塞非阻塞的區別(超級重要)
        • (2) 文件系統的理解(EXT4,XFS,BTRFS)
        • (3) EPOLL的介紹和了解
        • (4) IO復用的三種方法(select,poll,epoll)深入理解,包括三者區別,內部原理實現?
        • (5) Epoll的ET模式和LT模式(ET的非阻塞)
        • (6) 查詢進程占用CPU的命令(注意要了解到used,buf,代表意義)
        • (7) linux的其他常見命令(kill,find,cp等等)
        • (8) shell腳本用法
        • (9) 硬連接和軟連接的區別
        • (10) 文件權限怎么看(rwx)
        • (11) 文件的三種時間(mtime, atime,ctime),分別在什么時候會改變
        • (12) Linux監控網絡帶寬的命令,查看特定進程的占用網絡資源情況命令
        • (13)Linux中線程的同步方式有哪些?
        • (14)怎么修改一個文件的權限
        • (15)查看文件內容常用命令
        • (16)怎么找出含有關鍵字的前后4行
        • (17)Linux的GDB調試
        • (18)coredump是什么 怎么才能coredump
        • (19)tcpdump常用命令
        • (20) crontab命令
        • (21) 查看后臺進程
  • 5. 操作系統
        • (1) 進程與線程的區別和聯系(重點)
        • (2) Linux理論上最多可以創建多少個進程?一個進程可以創建多少線程,和什么有關
        • (3) 馮諾依曼結構有哪幾個模塊?分別對應現代計算機的哪幾個部分?(百度安全一面)
        • (4) 進程之間的通信方法有哪幾種 (重點)
        • (5) 進程調度方法詳細介紹
        • (6) 進程的執行過程是什么樣的,執行一個進程需要做哪些工作?
        • (6) 操作系統的內存管理說一下
        • (7) 實現一個LRU算法
        • (8) 死鎖產生的必要條件(怎么檢測死鎖,解決死鎖問題)
        • (9) 死鎖的恢復
        • (10)什么是饑餓
        • (11) 如果要你實現一個mutex互斥鎖你要怎么實現?
        • (12)線程之間的通信方式有哪些? 進程之間的同步方式又哪些?
        • (13) 什么時候用多進程,什么時候用多線程
        • (14) 文件讀寫使用的系統調用
        • (15) 孤兒進程和僵尸進程分別是什么,怎么形成的?
        • (16) 說一下PCB/說一下進程地址空間/
        • (17) 內核空間和用戶空間是怎樣區分的
        • (18) 多線程是如何同步的(尤其是如果項目中用到了多線程,很大可能會結合討論)
        • (19) 同一個進程內的線程會共享什么資源?
        • (20) 異常和中斷的區別
        • (21) 一般情況下在Linux/windows平臺下棧空間的大小
        • (22)虛擬內存的了解
        • (23)服務器高并發的解決方案
        • (24)協程了解嗎(高頻)
        • (25)那協程的底層是怎么實現的,怎么使用協程?
        • (26)進程的狀態以及轉換圖
        • (27)在執行malloc申請內存的時候,操作系統是怎么做的?/內存分配的原理說一下/malloc函數底層是怎么實現的?/進程是怎么分配內存的?
        • (28)什么是字節序?怎么判斷是大端還是小端?有什么用?
  • 6. 場景題/算法題
        • (0) leetcode hot100至少刷兩遍,劍指offer至少刷兩遍 重中之重!!
        • (1) 介紹熟悉的設計模式(單例,簡單工廠模式)
        • (2) 寫單例模式,線程安全版本
        • (3) 寫三個線程交替打印ABC
        • (4) 二維碼登錄的實現過程 場景題
        • (5) 不使用臨時變量實現swap函數
        • (6) 實現一個strcpy函數(或者memcpy),如果內存可能重疊呢
        • (7) 實現快排
        • (8) 實現一個堆排序
        • (8) 實現一個插入排序
        • (9) 快排存在的問題,如何優化
        • (10) 反轉一個鏈表(招銀網絡二面)
        • (11) Top K問題(可以采取的方法有哪些,各自優點?)(重點)
        • (12) 8G的int型數據,計算機的內存只有2G,怎么對它進行排序?(外部排序)(百度一面)
        • (13) 自己構建一棵二叉樹,使用帶有null標記的前序遍歷序列
        • (14) 介紹一下b樹和它的應用場景有哪些
        • (15) 介紹一下b+樹和它的應用場景有哪些
        • (16) 介紹一下紅黑樹和它的應用場景有哪些
        • (17) 怎么寫sql取表的前1000行數據(招銀網絡二面)
        • (18) N個骰子出現和為m的概率
        • (19) 海量數據問題(可參考左神的書)
        • (20) 一致性哈希
        • (21)希爾排序說一下/手撕
        • (22)Dijkstra算法說一下
        • (23)實現一個動態數組要怎么實現,說思路(騰訊teg一面)
        • (24)最小生成樹算法說一下
        • (25) 海量數據的bitmap使用原理
        • (26) 布隆過濾器原理與優點
        • (27) 布隆過濾器處理大規模問題時的持久化,包括內存大小受限、磁盤換入換出問題
        • (28)實現一個隊列,并且使它支持多線程,隊列有什么應用場景(阿里三面)
  • 7. 智力題
        • (1) 100層樓,只有2個雞蛋,想要判斷出那一層剛好讓雞蛋碎掉,給出策略(滴滴筆試中兩個鐵球跟這個是一類題)
        • (2) 毒藥問題,1000瓶水,其中有一瓶可以無限稀釋的毒藥,要快速找出哪一瓶有毒,需要幾只小白鼠
        • (3)
        • (4) 先手必勝策略問題:100本書,每次能夠拿1-5本,怎么拿能保證最后一次是你拿
        • (5) 放n只螞蟻在一條樹枝上,螞蟻與螞蟻之間碰到就各自往反方向走,問總距離或者時間。
        • (6) 瓶子換飲料問題:1000瓶飲料,3個空瓶子能夠換1瓶飲料,問最多能喝幾瓶
        • (7)在24小時里面時針分針秒針可以重合幾次
        • (8) 有一個天平,九個砝碼,一個輕一些,用天平至少幾次能找到輕的?
        • (9) 有十組砝碼每組十個,每個砝碼重10g,其中一組每個只有9g,有能顯示克數的秤最少幾次能找到輕的那一組砝碼?
        • (10)生成隨機數問題:給定生成1到5的隨機數Rand5(),如何得到生成1到7的隨機數函數Rand7()?
        • (11) 賽馬:有25匹馬,每場比賽只能賽5匹,至少要賽多少場才能找到最快的3匹馬?
        • (12) 燒 香/繩子/其他 確定時間問題:有兩根不均勻的香,燃燒完都需要一個小時,問怎么確定15分鐘的時長?
        • (13) 掰巧克力問題 N*M塊巧克力,每次掰一塊的一行或一列,掰成1*1的巧克力需要多少次?(1000個人參加辯論賽,1V1,輸了就退出,需要安排多少場比賽)(快手提前批)
  • 8. 大數據
        • 1. 介紹一下Hadoop
        • 2. 說一下MapReduce的運行機制
        • 3. 介紹一下kafka
        • 4. 為什么kafka吞吐量高?/介紹一下零拷貝
        • 5. 介紹一下spark
        • 6. 介紹一下spark-streaming
        • 7. spark的transformation和action有什么區別
        • 8. spark常用的算子說幾個
        • 9. 如何保證kafka的消息不丟失
        • 10. kafka如何選舉leader
        • 11. 說下spark中的寬依賴和窄依賴
        • 12. 說下spark中stage是依照什么劃分的
        • 13. spark的內存管理是怎樣的
        • 14. spark的容錯機制是什么樣的
  • 9. HR面
        • 1. 自我介紹
        • 2. 項目中遇到的最大難點
        • 3. 項目中的收獲
        • 4. 可以實習的時間,實習時長
        • 5. 哪里人
        • 6. 說一下自己的性格
        • 7. 你的優缺點是什么
        • 8. 有什么興趣愛好,畫的怎么樣/球打的如何/游戲打的怎么樣
        • 9. 看過最好的一本書是什么
        • 10. 學習技術中有什么難點
        • 11. 怎么看待加班
        • 12. 覺得深圳怎么樣(或者其他地點)
        • 13. 遇見過最大的挫折是什么,怎么解決的
        • 14. 職業規劃
        • 15. 目前的offer情況
        • 16. 你最大的優勢和劣勢是什么
        • 17. 介紹在項目里面充當的角色
        • 18. 介紹一下本科獲得的全國賽獎項的情況
        • 19. 最有成就感的事情/最驕傲的一件事情
        • 20. 在實驗室中擔任什么角色,參加的XXX能聊聊嗎
        • 22. 用兩個詞來形容自己

寫在前面

關于實習

博主在投遞秋招提前批的時候簡歷上是沒有實習經歷的。大三的暑假在參加保研夏令營;研一春招的時候手頭有一個做的差不多了的科研課題,當時選擇了相信導師跟著他改論文投會議,但是事實證明這個選擇是錯誤的,既沒有投中,又錯過了春招找實習的機會。不過在春招的末尾匆匆面了幾家,都掛了,最接近offer的是騰訊WXG的面試,已經hr面結束,但是最后泡池子泡掛了。聽說實習對秋招十分重要,當時投遞秋招提前批的時候我也是很絕望的,不過求生欲逼著我不斷投遞不斷面試,最后也拿到了好幾個offer。因此沒有實習并不是致命缺點,只不過有的話更好,跟面試官聊實習和項目就可以聊很久,因為一場技術面試一般一個小時,聊項目聊掉半個小時,剩下半個小時手撕算法,就不會問那么多奇奇怪怪的基礎知識啦。

關于簡歷

  • 一份技術簡歷主要包括姓名聯系方式等基本信息,意向崗位,教育背景,專業技能,實習經歷,項目經歷和獲獎經歷等信息。最大的用途就是方便hr篩選,和面試官了解你和提出問題。
  • 簡歷可以用ps做,可以用PPT做,可以用word做,可以用latex做,可以用markdown做,但是最后一定要導出成PDF的。我的簡歷從最初到現在改動了十幾次。首先到網上尋找簡歷模板(推薦超級簡歷,很不錯)去完成簡歷初稿,然后請身邊經驗豐富的朋友指點,在以后的每次面試后針對性的再次修改,但是要記得更新了之后在每個投遞的公司網站上都更新一遍。關于簡歷的注意點就不在這里說了,網上有很多相關教程,各有各的道理,但是不存在一個完全完美的模板,都是因人而異的,我在這里只提一點:一定要簡潔明了,不要花里胡哨,企圖標新立異。
  • 下面附上我的簡歷示例,給大家做一個參考,簡歷的內容我進行了一部分的修改。博主秋招投了15個公司/崗位,只有京東提前批一家被掛簡歷(但京東正式批沒掛),在這之中簡歷有很大的功勞。但是再次重申簡歷是十分因人而異的,我的簡歷也不一定完全標準,僅作參考。鏈接: 網盤鏈接
    提取碼:p786

關于投遞

因為一般情況下不會每個公司能夠通過簡歷篩選的,很多人會因為害怕被掛或者嫌麻煩而不敢投遞簡歷, 所以我推薦首先在心里給自己定下一個數量上的指標,先投滿20個,但凡有機會的都無腦投就是了,其他的情況以后再考慮。先就業再擇業。

關于面試準備

我在秋招之前用了兩個月的時間進行準備,但并不是兩個月過去了才投簡歷,而是準備的過程中一有機會就投,因為從投遞到發起筆試/面試通常要隔兩個星期,等到你完全準備好了,別人已經面的熱火朝天了。

  • 刷算法題。leetcode的hot100+劍指offer 刷兩遍就足夠,第一遍刷的時候自己先想十分鐘,如果沒有思路直接看題解。因為mid難度以上的題沒有刷過一般是不會有思路的,在一道題上耗太多時間不明智,而且容易有挫敗感。第二遍已經知道了思路,就要求在十分鐘內寫出來bug free的代碼,確保面試的時候也能有這個效率。
  • 刷面經。尋找和整理面經,對面經里面的每一條你不懂的題目,都去網上或者課本搜答案,并且把相關的原理搜一下,不要有僥幸心理,覺得這么難不可能問到我的,因為很可能它就是你即將被問到的題目,親測遇到的概率極大。
  • 復習項目。把自己值得展示的項目選2到3個好好進行復習。準備以下幾個點:介紹一下自己的項目;項目的具體流程是怎樣的?;項目有哪些難點和挑戰?;你們團隊又是如何克服這些難點的?;你主要負責的部分是什么?;項目中有哪些收獲?
  • 準備自我介紹。大概一分鐘的自我介紹。一定要背的流利,因為這一般是面試的第一步,如果自我介紹背的流暢,那么就是一個好的開端,接下來自己的心理壓力也就沒那么大了。

1. 語言基礎 (C/C++)

(0) 指針和引用的區別

  • 指針是一個新的變量,指向另一個變量的地址,我們可以通過訪問這個地址來修改另一個變量;而引用是一個別名,對引用的操作就是對變量的本身進行操作
  • 指針可以有多級,引用只有一級
  • 傳參的時候,使用指針的話需要解引用才能對參數進行修改,而使用引用可以直接對參數進行修改
  • 指針的大小一般是4個字節,引用的大小取決于被引用對象的大小
  • 指針可以為空,引用不可以。

(1)在函數參數傳遞的時候,什么時候使用指針,什么時候使用引用?

  • 需要返回函數內局部變量的內存的時候用指針。使用指針傳參需要開辟內存,用完要記得釋放指針,不然會內存泄漏。而返回局部變量的引用是沒有意義的
  • 對棧空間大小比較敏感(比如遞歸)的時候使用引用。使用引用傳遞不需要創建臨時變量,開銷要更小
  • 類對象作為參數傳遞的時候使用引用,這是C++類對象傳遞的標準方式

(2) 堆和棧有什么區別

  • 從定義上:堆是由new和malloc開辟的一塊內存,由程序員手動管理,棧是編譯器自動管理的內存,存放函數的參數和局部變量。
  • 堆空間因為會有頻繁的分配釋放操作,會產生內存碎片
  • 堆的生長空間向上,地址越來越大,棧的生長空間向下,地址越來越小

(3)堆快一點還是棧快一點?(字節提前批一面)

棧快一點。因為操作系統會在底層對棧提供支持,會分配專門的寄存器存放棧的地址,棧的入棧出棧操作也十分簡單,并且有專門的指令執行,所以棧的效率比較高也比較快。而堆的操作是由C/C++函數庫提供的,在分配堆內存的時候需要一定的算法尋找合適大小的內存。并且獲取堆的內容需要兩次訪問,第一次訪問指針,第二次根據指針保存的地址訪問內存,因此堆比較慢。

(4) new和delete是如何實現的,new 與 malloc的異同處

在new一個對象的時候,首先會調用malloc為對象分配內存空間,然后調用對象的構造函數。delete會調用對象的析構函數,然后調用free回收內存。

new與malloc都會分配空間,但是new還會調用對象的構造函數進行初始化,malloc需要給定空間大小,而new只需要對象名

(5)既然有了malloc/free,C++中為什么還需要new/delete呢?

詳見:https://blog.csdn.net/leikun153/article/details/80612130

  • malloc/free和new/delete都是用來申請內存和回收內存的。
  • 在對非基本數據類型的對象使用的時候,對象創建的時候還需要執行構造函數,銷毀的時候要執行析構函數。而malloc/free是庫函數,是已經編譯的代碼,所以不能把構造函數和析構函數的功能強加給malloc/free。

(6) C和C++的區別

包括但不限于:

  • C是面向過程的語言,C++是面向對象的語言,C++有“封裝,繼承和多態”的特性。封裝隱藏了實現細節,使得代碼模塊化。繼承通過子類繼承父類的方法和屬性,實現了代碼重用。多態則是“一個接口,多個實現”,通過子類重寫父類的虛函數,實現了接口重用。
  • C和C++內存管理的方法不一樣,C使用malloc/free,C++除此之外還用new/delete
  • C++中還有函數重載和引用等概念,C中沒有

(7)delete和delete[]的區別

  • delete只會調用一次析構函數,而delete[]會調用每個成員的析構函數

  • 用new分配的內存用delete釋放,用new[]分配的內存用delete[]釋放

(8) C++、Java的聯系與區別,包括語言特性、垃圾回收、應用場景等(java的垃圾回收機制)

包括但不限于:

  • C++ 和Java都是面向對象的語言,C++是編譯成可執行文件直接運行的,JAVA是編譯之后在JAVA虛擬機上運行的,因此JAVA有良好的跨平臺特性,但是執行效率沒有C++ 高。
  • C++的內存管理由程序員手動管理,JAVA的內存管理是由Java虛擬機完成的,它的垃圾回收使用的是標記-回收算法
  • C++有指針,Java沒有指針,只有引用
  • JAVA和C++都有構造函數,但是C++有析構函數但是Java沒有

(9)C++和python的區別

包括但不限于:

  • python是一種腳本語言,是解釋執行的,而C++是編譯語言,是需要編譯后在特定平臺運行的。python可以很方便的跨平臺,但是效率沒有C++高。
  • python使用縮進來區分不同的代碼塊,C++使用花括號來區分
  • C++中需要事先定義變量的類型,而python不需要,python的基本數據類型只有數字,布爾值,字符串,列表,元組等等
  • python的庫函數比C++的多,調用起來很方便
  • (10) Struct和class的區別

    • 使用struct時,它的成員的訪問權限默認是public的,而class的成員默認是private的
    • struct的繼承默認是public繼承,而class的繼承默認是private繼承
    • class可以用作模板,而struct不能

    (11) define 和const的聯系與區別(編譯階段、安全性、內存占用等)

    聯系:它們都是定義常量的一種方法。

    區別:

    • define定義的常量沒有類型,只是進行了簡單的替換,可能會有多個拷貝,占用的內存空間大,const定義的常量是有類型的,存放在靜態存儲區,只有一個拷貝,占用的內存空間小。
    • define定義的常量是在預處理階段進行替換,而const在編譯階段確定它的值。
    • define不會進行類型安全檢查,而const會進行類型安全檢查,安全性更高。
    • const可以定義函數而define不可以。

    (12) 在C++中const的用法(定義,用途)

    • const修飾類的成員變量時,表示常量不能被修改
    • const修飾類的成員函數,表示該函數不會修改類中的數據成員,不會調用其他非const的成員函數

    (13) C++中的static用法和意義

    static的意思是靜態的,可以用來修飾變量,函數和類成員。

    • 變量:被static修飾的變量就是靜態變量,它會在程序運行過程中一直存在,會被放在靜態存儲區。局部靜態變量的作用域在函數體中,全局靜態變量的作用域在這個文件里。

    • 函數:被static修飾的函數就是靜態函數,靜態函數只能在本文件中使用,不能被其他文件調用,也不會和其他文件中的同名函數沖突。

    • 類:而在類中,被static修飾的成員變量是類靜態成員,這個靜態成員會被類的多個對象共用。被static修飾的成員函數也屬于靜態成員,不是屬于某個對象的,訪問這個靜態函數不需要引用對象名,而是通過引用類名來訪問。

    【note】靜態成員函數要訪問非靜態成員時,要用過對象來引用。局部靜態變量在函數調用結束后也不會被回收,會一直在程序內存中,直到該函數再次被調用,它的值還是保持上一次調用結束后的值。

    注意和const的區別。const強調值不能被修改,而static強調唯一的拷貝,對所有類的對象都共用。

    (14) 計算下面幾個類的大小:

    class A {}; int main(){cout<<sizeof(A)<<endl;// 輸出 1;A a; cout<<sizeof(a)<<endl;// 輸出 1;return 0; }

    空類的大小是1, 在C++中空類會占一個字節,這是為了讓對象的實例能夠相互區別。具體來說,空類同樣可以被實例化,并且每個實例在內存中都有獨一無二的地址,因此,編譯器會給空類隱含加上一個字節,這樣空類實例化之后就會擁有獨一無二的內存地址。當該空白類作為基類時,該類的大小就優化為0了,子類的大小就是子類本身的大小。這就是所謂的空白基類最優化。

    空類的實例大小就是類的大小,所以sizeof(a)=1字節,如果a是指針,則sizeof(a)就是指針的大小,即4字節。

    class A { virtual Fun(){} }; int main(){cout<<sizeof(A)<<endl;// 輸出 4(32位機器)/8(64位機器);A a; cout<<sizeof(a)<<endl;// 輸出 4(32位機器)/8(64位機器);return 0; }

    因為有虛函數的類對象中都有一個虛函數表指針 __vptr,其大小是4字節

    class A { static int a; }; int main(){cout<<sizeof(A)<<endl;// 輸出 1;A a; cout<<sizeof(a)<<endl;// 輸出 1;return 0; }

    靜態成員存放在靜態存儲區,不占用類的大小, 普通函數也不占用類大小

    class A { int a; }; int main(){cout<<sizeof(A)<<endl;// 輸出 4;A a; cout<<sizeof(a)<<endl;// 輸出 4;return 0; } class A { static int a; int b; };; int main(){cout<<sizeof(A)<<endl;// 輸出 4;A a; cout<<sizeof(a)<<endl;// 輸出 4;return 0; }

    靜態成員a不占用類的大小,所以類的大小就是b變量的大小 即4個字節

    (15) C++的STL介紹(這個系列也很重要,建議侯捷老師的這方面的書籍與視頻),其中包括內存管理allocator,函數,實現機理,多線程實現等

    C++ STL從廣義來講包括了三類:算法,容器和迭代器。

    • 算法包括排序,復制等常用算法,以及不同容器特定的算法。
    • 容器就是數據的存放形式,包括序列式容器和關聯式容器,序列式容器就是list,vector等,關聯式容器就是set,map等。
    • 迭代器就是在不暴露容器內部結構的情況下對容器的遍歷。

    (16) STL源碼中的hash表的實現

    STL中的hash表就unordered_map。使用的是哈希進行實現(注意與map的區別)。它記錄的鍵是元素的哈希值,通過對比元素的哈希值來確定元素的值。

    unordered_map的底層實現是hashtable,采用開鏈法(也就是用桶)來解決哈希沖突,當桶的大小超過8時,就自動轉為紅黑樹進行組織。

    (17)解決哈希沖突的方式?

  • 線性探查。該元素的哈希值對應的桶不能存放元素時,循序往后一一查找,直到找到一個空桶為止,在查找時也一樣,當哈希值對應位置上的元素與所要尋找的元素不同時,就往后一一查找,直到找到吻合的元素,或者空桶。
  • 二次探查。該元素的哈希值對應的桶不能存放元素時,就往后尋找12,22,32,42…i^2個位置。
  • 雙散列函數法。當第一個散列函數發生沖突的時候,使用第二個散列函數進行哈希,作為步長。
  • 開鏈法。在每一個桶中維護一個鏈表,由元素哈希值尋找到這個桶,然后將元素插入到對應的鏈表中,STL的hashtable就是采用這種實現方式。
  • 建立公共溢出區。當發生沖突時,將所有沖突的數據放在公共溢出區。
  • (18) STL中unordered_map和map的區別

    • unordered_map是使用哈希實現的,占用內存比較多,查詢速度比較快,是常數時間復雜度。它內部是無序的,需要實現==操作符。
    • map底層是采用紅黑樹實現的,插入刪除查詢時間復雜度都是O(log(n)),它的內部是有序的,因此需要實現比較操作符(<)。

    (19) STL中vector的實現

    STL中的vector是封裝了動態數組的順序容器。不過與動態數組不同的是,vector可以根據需要自動擴大容器的大小。具體策略是每次容量不夠用時重新申請一塊大小為原來容量兩倍的內存,將原容器的元素拷貝至新容器,并釋放原空間,返回新空間的指針。

    在原來空間不夠存儲新值時,每次調用push_back方法都會重新分配新的空間以滿足新數據的添加操作。如果在程序中頻繁進行這種操作,還是比較消耗性能的。

    (20) vector使用的注意點及其原因,頻繁對vector調用push_back()對性能的影響和原因。

    如果需要頻繁插入,最好先指定vector的大小,因為vector在容器大小不夠用的時候會重新申請一塊大小為原容器兩倍的空間,并將原容器的元素拷貝到新容器中,并釋放原空間,這個過程是十分耗時和耗內存的。頻繁調用push_back()會使得程序花費很多時間在vector擴容上,會變得很慢。這種情況可以考慮使用list。

    (21)C++中vector和list的區別

    vector和數組類似,擁有一段連續的內存空間。vector申請的是一段連續的內存,當插入新的元素內存不夠時,通常以2倍重新申請更大的一塊內存,將原來的元素拷貝過去,釋放舊空間。因為內存空間是連續的,所以在進行插入和刪除操作時,會造成內存塊的拷貝,時間復雜度為o(n)。

    list是由雙向鏈表實現的,因此內存空間是不連續的。只能通過指針訪問數據,所以list的隨機存取非常沒有效率,時間復雜度為o(n); 但由于鏈表的特點,能高效地進行插入和刪除。

    vector擁有一段連續的內存空間,能很好的支持隨機存取,因此vector::iterator支持“+”,“+=”,“<”等操作符。

    list的內存空間可以是不連續,它不支持隨機訪問,因此list::iterator則不支持“+”、“+=”、“<”等

    vector::iterator和list::iterator都重載了“++”運算符。

    總之,如果需要高效的隨機存取,而不在乎插入和刪除的效率,使用vector;

    如果需要大量的插入和刪除,而不關心隨機存取,則應使用list。

    (22) C++中的重載和重寫的區別:

    • 重載(overload)是指函數名相同,參數列表不同的函數實現方法。它們的返回值可以不同,但返回值不可以作為區分不同重載函數的標志。
    • 重寫(overwide)是指函數名相同,參數列表相同,只有方法體不相同的實現方法。一般用于子類繼承父類時對父類方法的重寫。子類的同名方法屏蔽了父類方法的現象稱為隱藏。

    詳見:https://blog.csdn.net/weixin_30379911/article/details/99497160

    (23) C ++內存管理(熱門問題)

    詳見:https://blog.csdn.net/qq_43152052/article/details/98889139

    在C++中,內存分成5個區,他們分別是堆、棧、全局/靜態存儲區和常量存儲區和代碼區。

    • 棧,在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置于處理器的指令集中,效率很高,但是分配的內存容量有限。
    • 堆,就是那些由new分配的內存塊,他們的釋放編譯器不去管,由我們的應用程序去控制,一般一個new就要對應一個delete。如果程序員沒有釋放掉,那么在程序結束后,操作系統會自動回收。
    • 全局/靜態存儲區,內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。它主要存放靜態數據(局部static變量,全局static變量)、全局變量和常量。
    • 常量存儲區,這是一塊比較特殊的存儲區,他們里面存放的是常量字符串,不允許修改。
    • 代碼區,存放程序的二進制代碼

    關于這個有很多種說法,有的會增加一個自由存儲區,存放malloc分配得到的內存,與堆相似。

    (24) 介紹面向對象的三大特性,并且舉例說明每一個。

    面向對象的三大特性是:封裝,繼承和多態。

    • 封裝隱藏了類的實現細節和成員數據,實現了代碼模塊化,如類里面的private和public;
    • 繼承使得子類可以復用父類的成員和方法,實現了代碼重用;
    • 多態則是“一個接口,多個實現”,通過父類調用子類的成員,實現了接口重用,如父類的指針指向子類的對象。

    (25) 多態的實現(和下個問題一起回答)

    C++ 多態包括編譯時多態和運行時多態,編譯時多態體現在函數重載和模板上,運行時多態體現在虛函數上。

    • 虛函數:在基類的函數前加上virtual關鍵字,在派生類中重寫該函數,運行時將會根據對象的實際類型來調用相應的函數。如果對象類型是派生類,就調用派生類的函數;如果對象類型是基類,就調用基類的函數.

    (26) C++虛函數相關(虛函數表,虛函數指針),虛函數的實現原理(熱門,重要)

    C++的虛函數是實現多態的機制。它是通過虛函數表實現的,虛函數表是每個類中存放虛函數地址的指針數組,類的實例在調用函數時會在虛函數表中尋找函數地址進行調用,如果子類覆蓋了父類的函數,則子類的虛函數表會指向子類實現的函數地址,否則指向父類的函數地址。一個類的所有實例都共享同一張虛函數表。

    詳見:C++虛函數表剖析

    • 如果多重繼承和多繼承的話,子類的虛函數表長什么樣子?
      多重繼承的情況下越是祖先的父類的虛函數更靠前,多繼承的情況下越是靠近子類名稱的類的虛函數在虛函數表中更靠前。詳見:https://blog.csdn.net/qq_36359022/article/details/81870219

    (27) 實現編譯器處理虛函數表應該如何處理

    編譯器處理虛函數的方法是:
    如果類中有虛函數,就將虛函數的地址記錄在類的虛函數表中。派生類在繼承基類的時候,如果有重寫基類的虛函數,就將虛函數表中相應的函數指針設置為派生類的函數地址,否則指向基類的函數地址。
    為每個類的實例添加一個虛表指針(vptr),虛表指針指向類的虛函數表。實例在調用虛函數的時候,通過這個虛函數表指針找到類中的虛函數表,找到相應的函數進行調用。
    詳見:虛函數的作用及其底層實現機制

    (28) 基類的析構函數一般寫成虛函數的原因

    首先析構函數可以為虛函數,當析構一個指向子類的父類指針時,編譯器可以根據虛函數表尋找到子類的析構函數進行調用,從而正確釋放子類對象的資源。

    如果析構函數不被聲明成虛函數,則編譯器實施靜態綁定,在刪除指向子類的父類指針時,只會調用父類的析構函數而不調用子類析構函數,這樣就會造成子類對象析構不完全造成內存泄漏。

    (29) 構造函數為什么一般不定義為虛函數

    1)因為創建一個對象時需要確定對象的類型,而虛函數是在運行時確定其類型的。而在構造一個對象時,由于對象還未創建成功,編譯器無法知道對象的實際類型,是類本身還是類的派生類等等

    2)虛函數的調用需要虛函數表指針,而該指針存放在對象的內存空間中;若構造函數聲明為虛函數,那么由于對象還未創建,還沒有內存空間,更沒有虛函數表地址用來調用虛函數即構造函數了

    (30) 構造函數或者析構函數中調用虛函數會怎樣

    在構造函數中調用虛函數,由于當前對象還沒有構造完成,此時調用的虛函數指向的是基類的函數實現方式。

    在析構函數中調用虛函數,此時調用的是子類的函數實現方式。

    (31) 純虛函數

    純虛函數是只有聲明沒有實現的虛函數,是對子類的約束,是接口繼承

    包含純虛函數的類是抽象類,它不能被實例化,只有實現了這個純虛函數的子類才能生成對象

    使用場景:當這個類本身產生一個實例沒有意義的情況下,把這個類的函數實現為純虛函數,比如動物可以派生出老虎兔子,但是實例化一個動物對象就沒有意義。并且可以規定派生的子類必須重寫某些函數的情況下可以寫成純虛函數。

    (32) 靜態綁定和動態綁定的介紹

    詳見:C++中的靜態綁定和動態綁定

    靜態綁定也就是將該對象相關的屬性或函數綁定為它的靜態類型,也就是它在聲明的類型,在編譯的時候就確定。在調用的時候編譯器會尋找它聲明的類型進行訪問。

    動態綁定就是將該對象相關的屬性或函數綁定為它的動態類型,具體的屬性或函數在運行期確定,通常通過虛函數實現動態綁定。

    (33) 深拷貝和淺拷貝的區別(舉例說明深拷貝的安全性)

    淺拷貝就是將對象的指針進行簡單的復制,原對象和副本指向的是相同的資源。

    而深拷貝是新開辟一塊空間,將原對象的資源復制到新的空間中,并返回該空間的地址。

    深拷貝可以避免重復釋放和寫沖突。例如使用淺拷貝的對象進行釋放后,對原對象的釋放會導致內存泄漏或程序崩潰。

    (34) 對象復用的了解,零拷貝的了解

    對象復用指得是設計模式,對象可以采用不同的設計模式達到復用的目的,最常見的就是繼承和組合模式了。

    零拷貝指的是在進行操作時,避免CPU從一處存儲拷貝到另一處存儲。在Linux中,我們可以減少數據在內核空間和用戶空間的來回拷貝實現,比如通過調用mmap()來代替read調用。

    用程序調用mmap(),磁盤上的數據會通過DMA被拷貝的內核緩沖區,接著操作系統會把這段內核緩沖區與應用程序共享,這樣就不需要把內核緩沖區的內容往用戶空間拷貝。應用程序再調用write(),操作系統直接將內核緩沖區的內容拷貝到socket緩沖區中,這一切都發生在內核態,最后,socket緩沖區再把數據發到網卡去。

    (35) 介紹C++所有的構造函數

    C++中的構造函數主要有三種類型:默認構造函數、重載構造函數和拷貝構造函數

    • 默認構造函數是當類沒有實現自己的構造函數時,編譯器默認提供的一個構造函數。
    • 重載構造函數也稱為一般構造函數,一個類可以有多個重載構造函數,但是需要參數類型或個數不相同。可以在重載構造函數中自定義類的初始化方式。
    • 拷貝構造函數是在發生對象復制的時候調用的。

    (36) 什么情況下會調用拷貝構造函數(三種情況)

    • 對象以值傳遞的方式傳入函數參數

      如 void func(Dog dog){};

    • 對象以值傳遞的方式從函數返回

      如 Dog func(){ Dog d; return d;}

    • 對象需要通過另外一個對象進行初始化

    詳見:C++拷貝構造函數詳解

    (37) 結構體內存對齊方式和為什么要進行內存對齊?

    因為結構體的成員可以有不同的數據類型,所占的大小也不一樣。同時,由于CPU讀取數據是按塊讀取的,內存對齊可以使得CPU一次就可以將所需的數據讀進來。

    對齊規則:

    • 第一個成員在與結構體變量偏移量為0的地址
    • 其他成員變量要對齊到某個數字(對齊數)的整數倍的地址處。
    • 對齊數=編譯器默認的一個對齊數 與 該成員大小的較小值。
    • linux 中默認為4
    • vs 中的默認值為8
      結構體總大小為最大對齊數的整數倍(每個成員變量除了第一個成員都有一個對齊數)

    (38) 內存泄露的定義,如何檢測與避免?

    動態分配內存所開辟的空間,在使用完畢后未手動釋放,導致一直占據該內存,即為內存泄漏。

    造成內存泄漏的幾種原因:

    1)類的構造函數和析構函數中new和delete沒有配套

    2)在釋放對象數組時沒有使用delete[],使用了delete

    3)沒有將基類的析構函數定義為虛函數,當基類指針指向子類對象時,如果基類的析構函數不是virtual,那么子類的析構函數將不會被調用,子類的資源沒有正確釋放,因此造成內存泄露

    4)沒有正確的清楚嵌套的對象指針

    避免方法:

  • malloc/free要配套
  • 使用智能指針;
  • 將基類的析構函數設為虛函數;
  • (39) C++的智能指針有哪些

    C++中的智能指針有auto_ptr,shared_ptr,weak_ptr和unique_ptr。智能指針其實是將指針進行了封裝,可以像普通指針一樣進行使用,同時可以自行進行釋放,避免忘記釋放指針指向的內存地址造成內存泄漏。

    • auto_ptr是較早版本的智能指針,在進行指針拷貝和賦值的時候,新指針直接接管舊指針的資源并且將舊指針指向空,但是這種方式在需要訪問舊指針的時候,就會出現問題。
    • unique_ptr是auto_ptr的一個改良版,不能賦值也不能拷貝,保證一個對象同一時間只有一個智能指針。
    • shared_ptr可以使得一個對象可以有多個智能指針,當這個對象所有的智能指針被銷毀時就會自動進行回收。(內部使用計數機制進行維護)
    • weak_ptr是為了協助shared_ptr而出現的。它不能訪問對象,只能觀測shared_ptr的引用計數,防止出現死鎖。

    (40) 調試程序的方法

    • 通過設置斷點進行調試
    • 打印log進行調試
    • 打印中間結果進行調試

    (41) 遇到coredump要怎么調試

    coredump是程序由于異常或者bug在運行時異常退出或者終止,在一定的條件下生成的一個叫做core的文件,這個core文件會記錄程序在運行時的內存,寄存器狀態,內存指針和函數堆棧信息等等。對這個文件進行分析可以定位到程序異常的時候對應的堆棧調用信息。

    • 使用gdb命令對core文件進行調試

    以下例子在Linux上編寫一段代碼并導致segment fault 并產生core文件

    mkdir coredumpTest vim coredumpTest.cpp

    在編輯器內鍵入

    #include<stdio.h> int main(){int i;scanf("%d",i);//正確的應該是&i,這里使用i會導致segment faultprintf("%d\n",i);return 0; }

    編譯

    g++ coredumpTest.cpp -g -o coredumpTest

    運行

    ./coredumpTest

    使用gdb調試coredump

    gdb [可執行文件名] [core文件名]

    (42) inline關鍵字說一下 和宏定義有什么區別

    inline是內聯的意思,可以定義比較小的函數。因為函數頻繁調用會占用很多的棧空間,進行入棧出棧操作也耗費計算資源,所以可以用inline關鍵字修飾頻繁調用的小函數。編譯器會在編譯階段將代碼體嵌入內聯函數的調用語句塊中。

    1、內聯函數在編譯時展開,而宏在預編譯時展開

    2、在編譯的時候,內聯函數直接被嵌入到目標代碼中去,而宏只是一個簡單的文本替換。

    3、內聯函數可以進行諸如類型安全檢查、語句是否正確等編譯功能,宏不具有這樣的功能。

    4、宏不是函數,而inline是函數

    5、宏在定義時要小心處理宏參數,一般用括號括起來,否則容易出現二義性。而內聯函數不會出現二義性。

    6、inline可以不展開,宏一定要展開。因為inline指示對編譯器來說,只是一個建議,編譯器可以選擇忽略該建議,不對該函數進行展開。

    7、宏定義在形式上類似于一個函數,但在使用它時,僅僅只是做預處理器符號表中的簡單替換,因此它不能進行參數有效性的檢測,也就不能享受C++編譯器嚴格類型檢查的好處,另外它的返回值也不能被強制轉換為可轉換的合適的類型,這樣,它的使用就存在著一系列的隱患和局限性。

    (43) 模板的用法與適用場景 實現原理

    用template <typename T>關鍵字進行聲明,接下來就可以進行模板函數和模板類的編寫了

    編譯器會對函數模板進行兩次編譯:第一次編譯在聲明的地方對模板代碼本身進行編譯,這次編譯只會進行一個語法檢查,并不會生成具體的代碼。在第二次時對代碼進行參數替換后再進行編譯,生成具體的函數代碼。

    (44) 成員初始化列表的概念,為什么用成員初始化列表會快一些(性能優勢)?

    成員初始化列表就是在類或者結構體的構造函數中,在參數列表后以冒號開頭,逗號進行分隔的一系列初始化字段。如下:

    class A{ int id; string name; FaceImage face; A(int& inputID,string& inputName,FaceImage& inputFace):id(inputID),name(inputName),face(inputFace){} // 成員初始化列表 };

    因為使用成員初始化列表進行初始化的話,會直接使用傳入參數的拷貝構造函數進行初始化,省去了一次執行傳入參數的默認構造函數的過程,否則會調用一次傳入參數的默認構造函數。所以使用成員初始化列表效率會高一些。

    另外,有三種情況是必須使用成員初始化列表進行初始化的:

    • 常量成員的初始化,因為常量成員只能初始化不能賦值
    • 引用類型
    • 沒有默認構造函數的對象必須使用成員初始化列表的方式進行初始化

    詳見C++ 初始化列表

    (45) 用過C11嗎,知道C11新特性嗎?(有面試官建議熟悉C11)

    • 自動類型推導auto:auto的自動類型推導用于從初始化表達式中推斷出變量的數據類型。通過auto的自動類型推導,可以大大簡化我們的編程工作

    • nullptr
      :nullptr是為了解決原來C++中NULL的二義性問題而引進的一種新的類型,因為NULL實際上代表的是0,而nullptr是void*類型的

    • lambda表達式:它類似Javascript中的閉包,它可以用于創建并定義匿名的函數對象,以簡化編程工作。Lambda的語法如下:
      [函數對象參數](操作符重載函數參數)mutable或exception聲明->返回值類型{函數體}

    • thread類和mutex類

    • 新的智能指針 unique_ptr和shared_ptr

    • 更多詳見:https://blog.csdn.net/caogenwangbaoqiang/article/details/79438279

    (46) C++的調用慣例(簡單一點C++函數調用的壓棧過程)

    函數的調用過程:

    1)從棧空間分配存儲空間

    2)從實參的存儲空間復制值到形參棧空間

    3)進行運算

    形參在函數未調用之前都是沒有分配存儲空間的,在函數調用結束之后,形參彈出棧空間,清除形參空間。

    數組作為參數的函數調用方式是地址傳遞,形參和實參都指向相同的內存空間,調用完成后,形參指針被銷毀,但是所指向的內存空間依然存在,不能也不會被銷毀。

    當函數有多個返回值的時候,不能用普通的 return 的方式實現,需要通過傳回地址的形式進行,即地址/指針傳遞。

    (47) C++的四種強制轉換

    四種強制類型轉換操作符分別為:static_cast、dynamic_cast、const_cast、reinterpret_cast

    • 1)static_cast :
      用于各種隱式轉換。具體的說,就是用戶各種基本數據類型之間的轉換,比如把int換成char,float換成int等。以及派生類(子類)的指針轉換成基類(父類)指針的轉換。

      特性與要點:

    • 它沒有運行時類型檢查,所以是有安全隱患的。
    • 在派生類指針轉換到基類指針時,是沒有任何問題的,在基類指針轉換到派生類指針的時候,會有安全問題。
    • static_cast不能轉換const,volatile等屬性
    • 2)dynamic_cast:
      用于動態類型轉換。具體的說,就是在基類指針到派生類指針,或者派生類到基類指針的轉換。
      dynamic_cast能夠提供運行時類型檢查,只用于含有虛函數的類。
      dynamic_cast如果不能轉換返回NULL。
    • 3)const_cast:
      用于去除const常量屬性,使其可以修改 ,也就是說,原本定義為const的變量在定義后就不能進行修改的,但是使用const_cast操作之后,可以通過這個指針或變量進行修改; 另外還有volatile屬性的轉換。
    • 4)reinterpret_cast
      幾乎什么都可以轉,用在任意的指針之間的轉換,引用之間的轉換,指針和足夠大的int型之間的轉換,整數到指針的轉換等。但是不夠安全。

    (48)string的底層實現

    string繼承自basic_string,其實是對char*進行了封裝,封裝的string包含了char*數組,容量,長度等等屬性。

    string可以進行動態擴展,在每次擴展的時候另外申請一塊原空間大小兩倍的空間(2*n),然后將原字符串拷貝過去,并加上新增的內容。

    (49)一個函數或者可執行文件的生成過程或者編譯過程是怎樣的

    預處理,編譯,匯編,鏈接

    • 預處理: 對預處理命令進行替換等預處理操作
    • 編譯:代碼優化和生成匯編代碼
    • 匯編:將匯編代碼轉化為機器語言
    • 鏈接:將目標文件彼此鏈接起來

    (50)set,map和vector的插入復雜度

    set,map的插入復雜度就是紅黑樹的插入復雜度,是log(N)。

    unordered_set,unordered_map的插入復雜度是常數,最壞是O(N).

    vector的插入復雜度是O(N),最壞的情況下(從頭插入)就要對所有其他元素進行移動,或者擴容重新拷貝

    (51)定義和聲明的區別

    • 聲明是告訴編譯器變量的類型和名字,不會為變量分配空間

    • 定義就是對這個變量和函數進行內存分配和初始化。需要分配空間,同一個變量可以被聲明多次,但是只能被定義一次

    (52)typdef和define區別

    #define是預處理命令,在預處理是執行簡單的替換,不做正確性的檢查

    typedef是在編譯時處理的,它是在自己的作用域內給已經存在的類型一個別名

    (53)被free回收的內存是立即返還給操作系統嗎?為什么

    https://blog.csdn.net/YMY_mine/article/details/81180168

    不是的,被free回收的內存會首先被ptmalloc使用雙鏈表保存起來,當用戶下一次申請內存的時候,會嘗試從這些內存中尋找合適的返回。這樣就避免了頻繁的系統調用,占用過多的系統資源。同時ptmalloc也會嘗試對小塊內存進行合并,避免過多的內存碎片。

    (54)引用作為函數參數以及返回值的好處

    對比值傳遞,引用傳參的好處:

    1)在函數內部可以對此參數進行修改

    2)提高函數調用和運行的效率(因為沒有了傳值和生成副本的時間和空間消耗)

    如果函數的參數實質就是形參,不過這個形參的作用域只是在函數體內部,也就是說實參和形參是兩個不同的東西,要想形參代替實參,肯定有一個值的傳遞。函數調用時,值的傳遞機制是通過“形參=實參”來對形參賦值達到傳值目的,產生了一個實參的副本。即使函數內部有對參數的修改,也只是針對形參,也就是那個副本,實參不會有任何更改。函數一旦結束,形參生命也宣告終結,做出的修改一樣沒對任何變量產生影響。

    用引用作為返回值最大的好處就是在內存中不產生被返回值的副本。

    但是有以下的限制:

    1)不能返回局部變量的引用。因為函數返回以后局部變量就會被銷毀

    2)不能返回函數內部new分配的內存的引用。雖然不存在局部變量的被動銷毀問題,可對于這種情況(返回函數內部new分配內存的引用),又面臨其它尷尬局面。例如,被函數返回的引用只是作為一 個臨時變量出現,而沒有被賦予一個實際的變量,那么這個引用所指向的空間(由new分配)就無法釋放,造成memory leak

    3)可以返回類成員的引用,但是最好是const。因為如果其他對象可以獲得該屬性的非常量的引用,那么對該屬性的單純賦值就會破壞業務規則的完整性。

    (55)友元函數和友元類

    https://www.cnblogs.com/zhuguanhao/p/6286145.html

    友元提供了不同類的成員函數之間、類的成員函數和一般函數之間進行數據共享的機制。通過友元,一個不同函數或者另一個類中的成員函數可以訪問類中的私有成員和保護成員。友元的正確使用能提高程序的運行效率,但同時也破壞了類的封裝性和數據的隱藏性,導致程序可維護性變差。

    1)友元函數

    有元函數是定義在類外的普通函數,不屬于任何類,可以訪問其他類的私有成員。但是需要在類的定義中聲明所有可以訪問它的友元函數。

    #include <iostream>using namespace std;class A { public:friend void set_show(int x, A &a); //該函數是友元函數的聲明 private:int data; };void set_show(int x, A &a) //友元函數定義,為了訪問類A中的成員 {a.data = x;cout << a.data << endl; } int main(void) {class A a;set_show(1, a);return 0; }

    一個函數可以是多個類的友元函數,但是每個類中都要聲明這個函數。

    2)友元類

    友元類的所有成員函數都是另一個類的友元函數,都可以訪問另一個類中的隱藏信息(包括私有成員和保護成員)。
    但是另一個類里面也要相應的進行聲明

    #include <iostream>using namespace std;class A { public:friend class C; //這是友元類的聲明 private:int data; };class C //友元類定義,為了訪問類A中的成員 { public:void set_show(int x, A &a) { a.data = x; cout<<a.data<<endl;} };int main(void) {class A a;class C c;c.set_show(1, a);return 0; }

    使用友元類時注意:

    (1) 友元關系不能被繼承。

    (2) 友元關系是單向的,不具有交換性。若類B是類A的友元,類A不一定是類B的友元,要看在類中是否有相應的聲明。

    (3) 友元關系不具有傳遞性。若類B是類A的友元,類C是B的友元,類C不一定是類A的友元,同樣要看類中是否有相應的申明

    (56) 說一下volatile關鍵字的作用

    volatile的意思是“脆弱的”,表明它修飾的變量的值十分容易被改變,所以編譯器就不會對這個變量進行優化(CPU的優化是讓該變量存放到CPU寄存器而不是內存),進而提供穩定的訪問。每次讀取volatile的變量時,系統總是會從內存中讀取這個變量,并且將它的值立刻保存。

    (57) STL中的sort()算法是用什么實現的,stable_sort()呢

    STL中的sort是用快速排序和插入排序結合的方式實現的,stable_sort()是歸并排序。

    (58)vector會迭代器失效嗎?什么情況下會迭代器失效?

    https://www.cnblogs.com/qingjiaowoxiaoxioashou/p/5874572.html

    • 當vector在插入的時候,如果原來的空間不夠,會將申請新的內存并將原來的元素移動到新的內存,此時指向原內存地址的迭代器就失效了,first和end迭代器都失效
    • 當vector在插入的時候,end迭代器肯定會失效
    • 當vector在刪除的時候,被刪除元素以及它后面的所有元素迭代器都失效。

    (58)為什么C++沒有實現垃圾回收?

    • 首先,實現一個垃圾回收器會帶來額外的空間和時間開銷。你需要開辟一定的空間保存指針的引用計數和對他們進行標記mark。然后需要單獨開辟一個線程在空閑的時候進行free操作。
    • 垃圾回收會使得C++不適合進行很多底層的操作。

    2. 計網相關

    (1) 建立TCP服務器的各個系統調用

    建立TCP服務器連接的過程中主要通過以下系統調用序列來獲取某些函數,這些系統調用主要包括:socket(),bind(),listen(),accept(),send()和recv()。
    詳見:建立TCP 服務器的系統調用

    (2) 繼上一題,說明socket網絡編程有哪些系統調用?其中close是一次就能直接關閉的嗎,半關閉狀態是怎么產生的?

    socket() 創建套接字 bind() 綁定本機端口 connect() 建立連接 (TCP三次握手在調用這個函數時進行) listen() 監聽端口 accept() 接受連接 recv(), read(), recvfrom() 數據接收 send(), write(), sendto() 數據發送 close(), shutdown() 關閉套接字

    使用close()時,只有當套接字的引用計數為0的時候才會終止連接,而用shutdown()就可以直接關閉連接

    詳見:網絡編程Socket之TCP之close/shutdown詳解

    TCP連接與斷開詳解: https://www.cnblogs.com/felixzh/p/8359066.html

    (3) 對路由協議的了解與介紹。內部網關協議IGP包括RIP,OSPF,和外部網關協議EGP和BGP.

    • RIP“路由信息協議(Route Information Protocol)”的簡寫,主要傳遞路由信息,通過每隔30秒廣播一次路由表,維護相鄰路由器的位置關系,同時根據收到的路由表信息使用動態規劃的方式計算自己的路由表信息。RIP是一個距離矢量路由協議,最大跳數為16跳,16跳以及超過16跳的網絡則認為目標網絡不可達。

    • OSPF:詳見:https://zhuanlan.zhihu.com/p/41341540

    (4) UDP如何實現可靠傳輸

    因為UDP是無連接的協議,所以在傳輸層上無法保證可靠傳輸,要想實現可靠傳輸,只能從應用層實現。需要實現seq/ack機制,重傳機制和窗口確認機制。

    就要接收方收到UDP之后回復個確認包,發送方有個機制,收不到確認包就要重新發送,每個包有遞增的序號,接收方發現中間丟了包就要發重傳請求,當網絡太差時候頻繁丟包,防止越丟包越重傳的惡性循環,要有個發送窗口的限制,發送窗口的大小根據網絡傳輸情況調整,調整算法要有一定自適應性。

    作者:姚冬
    鏈接:https://www.zhihu.com/question/283995548/answer/661809748
    來源:知乎
    著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

    (5) TCP和UDP的區別

    • TCP是面向連接的協議,提供的是可靠傳輸,在收發數據前需要通過三次握手建立連接,使用ACK對收發的數據進行正確性檢驗。而UDP是無連接的協議,不管對方有沒有收到或者收到的數據是否正確。
    • TCP提供流量控制和擁塞控制,而UDP沒有。
    • TCP對系統資源的要求高于UDP,所以速度也比UDP慢。
    • TCP數據包是沒有邊界的,會出現粘包的問題,UDP包是獨立的,不會出現粘包問題。
    • 所以在應用方面,如果強調數據的完整性和正確性用TCP,當要求性能和速度的時候,使用UDP更加合適。

    注:單憑TCP是不能保證完整性的,要是有黑客偽造TCP包,是無法識別的。

    (6) TCP和UDP相關的協議與端口號

    TCP族的協議有HTTP,HTTPS,SMTP,TelNet,FTP等,UDP族的協議有DNS,DHCP等等。
    詳見:https://blog.csdn.net/qq_22080999/article/details/81105051

    (7) TCP(UDP,IP)等首部的認識(http請求報文構成)

    TCP的頭部大致包括:源端口,目的端口,序號,確認號,偏移位,標志位,校驗和等等

    UDP的頭部則包括:源端口,目的端口,長度,校驗和。

    IP數據包的頭部包括:源IP地址,目的IP地址,協議,校驗和,總長度等等

    詳見:https://blog.csdn.net/zhangliangzi/article/details/52554439

    (8) 網頁解析的過程與實現方法

    這里僅展示瀏覽器解析服務器響應的過程,URL解析和交互的完整過程在(9)

    • 首先是html文檔解析,瀏覽器會將html文檔生成解析樹,也就是DOM樹,它由dom元素以及屬性節點組成。
    • 然后瀏覽器加載過程中如果遇到了外部css文件或者圖片資源,還會另外發送請求來獲取css文件和資源,這個請求通常是異步的,不會影響html文檔的加載。
    • 不過如果瀏覽器在加載時遇到了js文件,則會掛起渲染的線程,等待js文件加載解析完畢才恢復html的渲染線程。
    • 然后是css解析,將css文件解析為樣式表對象來渲染DOM樹。

    (9) 在瀏覽器中輸入URL后執行的全部過程(如www.baidu.com)重點、熱門問題

  • 首先是域名解析,客戶端使用DNS協議將URL解析為對應的IP地址;
  • 然后建立TCP連接,客戶端與服務器通過三次握手建立TCP連接;
  • 接著是http連接,客戶端向服務器發送http連接請求; (http連接無需額外連接,直接通過已經建立的TCP連接發送)
  • 服務器對客戶端發來的http請求進行處理,并返回響應;
  • 客戶端接收到http響應,將結果渲染展示給用戶。
  • (10) 網絡層分片的原因與具體實現

    因為在鏈路層中幀的大小通常都有限制,比如在以太網中幀的最大大小(MTU)就是1500字節。如果IP數據包加上頭部后大小超過1500字節,就需要分片。

    IP分片和完整IP報文差不多擁有相同的IP頭,16位ID域對于每個分片都是一致的,這樣才能在重新組裝的時候識別出來自同一個IP報文的分片。在IP頭里面,16位識別號唯一記錄了一個IP包的ID,具有同一個ID的IP分片將會重新組裝;而13位片偏移則記錄了某IP片相對整個包的位置;而這兩個表中間的3位標志則標志著該分片后面是否還有新的分片。這三個標志就組成了IP分片的所有信息(將在后面介紹),接受方就可以利用這些信息對IP數據進行重新組織。
    詳見:https://blog.csdn.net/gettogetto/article/details/72851734

    (11) TCP的三次握手與四次揮手的詳細介紹(TCP連接建立與斷開是熱門問題)

    • 三次握手

    第一次握手:首先client給server發送連接請求報文,在這個報文中,包含了SYN=1,client_seq=任意值i,發送之后處于SYN-SENT狀態,這是第一次握手

    第二次握手:server端接收到了這個請求,并分配資源,同時給client返回一個ACK報文,這個報文中呢包含了這些字段,標志位SYN和ACK都為1,而小ack為i+1,此時位于SYN-RCVD狀態,這是第二次握手

    第三次握手:client收到server發來的ACK信息后呢,他會看到server發過來的小ack是i+1,這時他知道了server收到了消息,也給server回一個ACK報文,報文中同樣包含了ACK=1這樣的消息,同時呢,還包括了client_ack=k+1這樣的字段,這樣呢三次握手之后,連接就建立了,client進入established(已建立連接)狀態

    • 四次揮手斷開連接:

    TCP斷開連接通常是由一方主動,一方被動的,這里我們假設client主動,server被動
    第一次揮手:當client沒有數據要發送給server了,他會給server發送一個FIN報文,告訴server:“我已經沒有數據要發給你了,但是你要是還想給我發數據的話,你就接著發,但是你得告訴我你收到我的關閉信息了”,這是第一次揮手,揮手之后client進入FIN_WAIT_1的第一階段

    第二次揮手:當server收到client發來的FIN報文后,告訴client:“我收到你的FIN消息了,但是你等我發完的”此時給client返回一個ACK信息,并且呢ack=seq+1,這是第二次揮手,揮手之后呢server進入CLOSE_WAIT階段,而client收到之后處于FIN_WAIT_2第二階段

    第三次揮手:當server發完所有數據時,他會給client發送一個FIN報文,告訴client說“我傳完數據了,現在要關閉連接了”,然后呢server變成LAST_ACK狀態,等著client最后的ACK信息,這是第三次揮手

    第四次揮手:當client收到這個FIN報文時,他會對這個消息進行確認,即給server發ACK信息,但是它不相信網絡,怕server收不到信息,它會進入TIME_WAIT狀態,萬一server沒收到ACK消息它可以可以重傳,而當server收到這個ACK信息后,就正式關閉了tcp連接,處于CLOSED狀態,而client等待了2MSL這樣長時間后還沒等到消息,它知道server已經關閉連接了,于是乎他自己也斷開了,這是第四次揮手,這樣tcp連接就斷開了

    (12) TCP握手以及每一次握手客戶端和服務器端處于哪個狀態

    見上

    (13) 為什么使用三次握手,兩次握手可不可以?(重點,熱門問題)

    如果使用兩次握手的話,三次握手中的最后一次缺失,服務器不能確認客戶端的接收能力。

    舉兩個例子,第一種是黑客會偽造大量SYN請求發送給服務器,服務器立即確認并建立連接,分配資源,但是這一系列連接并不是真實存在的,這大大浪費了服務器的資源并且阻塞了正常用戶的連接,這種也叫SYN洪泛攻擊。第二種是服務器返回給客戶端的ACK數據包可能會在傳輸的過程中丟失,而客戶端沒有收到該ACK數據包而拒絕接收服務器接下來發送的數據,于是服務器一直在發送,客戶端一直在拒絕,形成死鎖。

    (14) TIME_WAIT的意義(為什么要等于2MSL)

    TIME_WAIT是指四次揮手中客戶端接收了服務端的FIN報文并發送ACK報文給服務器后,仍然需要等待2MSL時間的過程。雖然按道理,四個報文都發送完畢,我們可以直接進入CLOSE狀態了,但是我們必須假象網絡是不可靠的,有可以最后一個ACK丟失。如果客戶端發送的ACK發生丟失,服務器會再次發送FIN報文給客戶端,所以TIME_WAIT狀態就是用來重發可能丟失的ACK報文。

    (15) 超時重傳機制(不太高頻)

    (16) TCP怎么保證可靠性?

    (校序重流擁)

    • 校驗和
      發送的數據包的二進制相加然后取反,目的是檢測數據在傳輸過程中的任何變化。如果收到段的檢驗和有差錯,TCP將丟棄這個報文段和不確認收到此報文段。

    • 確認應答+序列號
      TCP給發送的每一個包進行編號,接收方對數據包進行排序,把有序數據傳送給應用層。

    • 超時重傳
      當TCP發出一個段后,它啟動一個定時器,等待目的端確認收到這個報文段。如果不能及時收到一個確認,將重發這個報文段。

    • 流量控制
      TCP連接的每一方都有固定大小的緩沖空間,TCP的接收端只允許發送端發送接收端緩沖區能接納的數據。當接收方來不及處理發送方的數據,能提示發送方降低發送的速率,防止包丟失。TCP使用的流量控制協議是可變大小的滑動窗口協議。
      接收方有即時窗口(滑動窗口),隨ACK報文發送

    • 擁塞控制
      當網絡擁塞時,減少數據的發送。
      發送方有擁塞窗口,發送數據前比對接收方發過來的即使窗口,取小

    慢啟動、擁塞避免、快速重傳、快速恢復

    (17) 流量控制的介紹,采用滑動窗口會有什么問題(死鎖可能,糊涂窗口綜合征)?

    所謂流量控制就是讓發送方發送速率不要過快,讓接收方來得及接收。利用TCP報文段中的窗口大小字段來控制發送方的發送窗口不大于接收方發回的窗口大小就可以實施流量控制。

    考慮一種特殊的情況,就是接收方若沒有緩存足夠使用,就會發送零窗口大小的報文,此時發送放將發送窗口設置為0,停止發送數據。之后接收方有足夠的緩存,發送了非零窗口大小的報文,但是這個報文在中途丟失的,那么發送方的發送窗口就一直為零導致死鎖。

    解決這個問題,TCP為每一個連接設置一個持續計時器(persistence timer)。只要TCP的一方收到對方的零窗口通知,就啟動該計時器,周期性的發送一個零窗口探測報文段。對方就在確認這個報文的時候給出現在的窗口大小(注意:TCP規定,即使設置為零窗口,也必須接收以下幾種報文段:零窗口探測報文段、確認報文段和攜帶緊急數據的報文段)。

    (18) tcp滑動窗口協議

    詳見 TCP-IP詳解:滑動窗口SlidingWindow和TCP滑動窗口

    TCP的滑動窗口用來控制接收方和發送方的發送速率,避免擁塞的發生。滑動窗口其實就是接收端的緩沖區大小,用來告訴發送方對它發送的數據有多大的緩沖空間。在接收方的滑動窗口已知的情況下,當接收方確認了連續的數據序列之后,發送方的滑動窗口向后滑動,發送下一個數據序列。

    接收方會在每個ACK數據包中附帶自己當前的接受窗口(滑動窗口)的大小,方便發送方進行控制。

    (19) 擁塞控制和流量控制的區別

    擁塞控制是防止過多的數據注入到網絡中,導致網絡發生擁塞;而流量控制是防止發送方一下子發送過多的數據到接收方,導致接收方緩存放不下。兩種算法都是對發送方的行為進行控制的。

    (20) TCP擁塞控制,算法名字?(極其重要)


    防止過多的數據注入到網絡中,這樣可以使網絡中的路由器或鏈路不致過載,擁塞控制自然也是控制發送者的流量,擁塞控制有四種算法,慢啟動、擁塞避免,快速重傳和快速恢復

    發送方維持一個擁塞窗口 cwnd ( congestion window )的狀態變量。擁塞窗口的大小取決于網絡的擁塞程度,并且動態地在變化。發送方讓自己的發送窗口等于擁塞窗口和接受窗口的較小值。

    (1)慢啟動。慢啟動算法的思路是當主機開始發送數據時,先以比較小的擁塞窗口進行發送,然后每次翻倍,也就是說,由小到大逐漸增加擁塞窗口的大小,而這個大小是指數增長的,即1、2、4、8、16
    *為了防止擁塞窗口cwnd增長過大引起網絡擁塞,還要另外設置一個慢啟動閾值ssthresh狀態變量,當擁塞窗口的大小超過慢啟動閾值的時候( cwnd > ssthresh 時),停止使用慢開始算法而改用擁塞避免算法

    (2)擁塞避免。擁塞避免算法的思路是讓擁塞窗口cwnd緩慢地增大,即每經過一個往返時間RTT就把發送方的擁塞窗口cwnd加1,而不是加倍。

    (3)快速重傳。當發送端連續收到三個重復的ack時,表示該數據段已經丟失,需要重發。此時慢啟動閾值ssth變為原來一半,擁塞窗口cwnd變為ssth+3,然后+1+1的發(每一輪rtt+1)

    (4)快速恢復。當超過設定的時間沒有收到某個報文段的ack時,表示網絡擁塞,慢啟動閾值ssth變為原來一半,擁塞窗口cwnd=1,進入慢啟動階段

    (21) http協議與TCP的區別與聯系

    聯系:Http協議是建立在TCP協議基礎之上的,當瀏覽器需要從服務器獲取網頁數據的時候,會發出一次Http請求。Http會通過TCP建立起一個到服務器的連接通道,當本次請求需要的數據傳輸完畢后,Http會立即將TCP連接斷開,這個過程是很短的。

    區別:HTTP和TCP位于不同的網絡分層。TCP是傳輸層的協議,定義的是數據傳輸和連接的規范,而HTTP是應用層的,定義的是數據的內容的規范。
    建立一個TCP請求需要進行三次握手,而由于http是建立在tcp連接之上的,建立一個http請求通常包含請求和響應兩個步驟。

    (22) http/1.0和http/1.1的區別

    HTTP 協議老的標準是 HTTP/1.0 ,目前最通用的標準是 HTTP/1.1 。
    HTTP1.0 只保持短暫的連接,瀏覽器的每次請求都需要與服務器建立一個 TCP 連接,但是最新的http/1.0加入了長連接,只需要在客戶端給服務器發送的http報文頭部加入Connection:keep-alive
    HTTP 1.1 支持持久連接,默認進行持久連接,在一個 TCP 連接上可以傳送多個 HTTP 請求和響應,減少了建立和關閉連接的消耗和延遲。

    (23) http的請求方法有哪些?get和post的區別。

    HTTP的請求方法包括GET,POST,PUT,DELETE四種基本方法。(四種方法中只有POST不是操作冪等性的)

    get和post的區別:

  • get方法不會修改服務器上的資源,它的查詢是沒有副作用的,而post有可能會修改服務器上的資源
  • get可以保存為書簽,可以用緩存來優化,而post不可以
  • get把請求附在url上,而post把參數附在http包的包體中
  • 瀏覽器和服務器一般對get方法所提交的url長度有限制,一般是1k或者2k,而對post方法所傳輸的參數大小限制為80k到4M不等
  • post可以傳輸二進制編碼的信息,get的參數一般只支持ASCII
  • (24) http的狀態碼 403 201等等是什么意思

    詳見 HTTP狀態碼的含義

    常見的狀態碼有:

    • 200 - 請求成功
    • 301 - 資源(網頁等)被永久轉移到其它URL
    • 404 - 請求的資源(網頁等)不存在
    • 500 - 內部服務器錯誤
    • 400 - 請求無效
    • 403 - 禁止訪問

    (25) http和https的區別,由http升級為https需要做哪些操作

    http 是超文本傳輸協議,信息是明文傳輸, https 則是具有安全性的 ssl 加密傳輸協議
    http 和 https 使用的是完全不同的連接方式,用的端口也不一樣,前者是 80 ,后者是 443
    http 的連接很簡單,是無狀態的; HTTPS 協議是由 SSL+HTTP 協議構建的可進行加密傳輸、身份認證的網絡協議,比http 協議安全。
    https 協議需要到 ca 申請證書,一般免費證書較少,因而需要一定費用
    https://www.cnblogs.com/wqhwe/p/5407468.html

    (26) https的具體實現,怎么確保安全性

    SSL是傳輸層的協議

    https包括非對稱加密和對稱加密兩個階段,在客戶端與服務器建立連接的時候使用非對稱加密,連接建立以后使用的是對稱加密。

  • 客戶使用https的URL訪問Web服務器,要求與Web服務器建立SSL連接
  • Web服務器收到客戶端請求后,會將網站的公鑰傳送一份給客戶端,私鑰自己保存。
  • 客戶端的瀏覽器根據雙方同意的安全等級,生成對稱加密使用的密鑰,稱為會話密鑰,然后利用網站的公鑰將會話密鑰加密,并傳送給網站
  • Web服務器利用自己的私鑰解密出會話密鑰。
  • Web服務器利用會話密鑰加密與客戶端之間的通信,這個過程是對稱加密的過程。
  • 服務器第一次傳給客戶端的公鑰其實是CA對網站信息進行加密的數字證書

    客戶端的對稱加密密鑰其實是三個隨機數的哈希(1. 客戶端第一次給服務端發送請求時附帶的隨機數 2. 服務器返回時的隨機數 3. 客戶端收到返回時的隨機數)

    (27) TCP三次握手時的第一次的seq序號是怎樣產生的 (字節提前批)

    第一次的序號是隨機序號,但也不是完全隨機,它是使用一個ISN算法得到的。

    seq = C + H (源IP地址,目的IP地址,源端口,目的端口)。其中,C是一個計時器,每隔一段時間值就會變大,H是消息摘要算法,輸入是一個四元組(源IP地址,目的IP地址,源端口,目的端口)。

    (28) 一個機器能夠使用的端口號上限是多少,為什么?可以改變嗎?那如果想要用的端口超過這個限制怎么辦?

    65536.因為TCP的報文頭部中源端口號和目的端口號的長度是16位,也就是可以表示2^16=65536個不同端口號,因此TCP可供識別的端口號最多只有65536個。但是由于0到1023是知名服務端口,所以實際上還要少1024個端口號。

    而對于服務器來說,可以開的端口號與65536無關,其實是受限于Linux可以打開的文件數量,并且可以通過MaxUserPort來進行配置。

    (29) 對稱密碼和非對稱密碼體系

    https://blog.csdn.net/qq_29689487/article/details/81634057

    • 對稱加密:加密和解密使用的密鑰是同一個
      • 優點:計算量小,算法速度快,加密效率高 缺點:密鑰容易泄漏。不同的會話需要不同的密鑰,管理起來很費勁
      • 常用算法:DES,3DES,IDEA,CR4,CR5,CR6,AES
    • 非對稱加密:需要公鑰和私鑰,公鑰用來加密,私鑰用來解密
      • 優點:安全,不怕泄漏 缺點:速度慢
      • 常用算法:RSA,ECC,DSA

    (30) 數字證書的了解(高頻)

    權威CA使用私鑰將網站A的信息和消息摘要(簽名S)進行加密打包形成數字證書。公鑰給客戶端。

    網站A將自己的信息和數字證書發給客戶端,客戶端用CA的公鑰對數字證書進行解密,得到簽名S,與手動將網站的信息進行消息摘要得到的結果S*進行對比,如果簽名一致就證明網站A可以信任。

    (31) 服務器出現大量close_wait的連接的原因以及解決方法

    close_wait狀態是在TCP四次揮手的時候收到FIN但是沒有發送自己的FIN時出現的,服務器出現大量close_wait狀態的原因有兩種:

    • 服務器內部業務處理占用了過多時間,都沒能處理完業務;或者還有數據需要發送;或者服務器的業務邏輯有問題,沒有執行close()方法
    • 服務器的父進程派生出子進程,子進程繼承了socket,收到FIN的時候子進程處理但父進程沒有處理該信號,導致socket的引用不為0無法回收

    處理方法:

    • 停止應用程序
    • 修改程序里的bug

    (32) 消息摘要算法列舉一下,介紹MD5算法,為什么MD5是不可逆的,有什么辦法可以加強消息摘要算法的安全性讓它不那么容易被破解呢?(百度安全一面)

    • 消息摘要算法有MD家族(MD2,MD4,MD5),SHA家族(SHA-1,SHA-256)和CRC家族(CRC8,CRC16,CRC32)等等

    • MD5算法介紹:
      MD5以512位分組來處理輸入的信息,且每一分組又被劃分為若干個小分組(16個32位子分組),經過一些列的處理后,算法輸出由四個散列值(32位分組組成的128位散列值。)

  • MD5首先將輸入的信息分成若干個512字節長度的分組,如果不夠就填充1和若干個0。
  • 對每個512字節的分組進行循環運算。使用四個幻數對第一個分組的數據進行四輪變換,得到四個變量。
  • 接下來對其中三個使用線性函數進行計算,與剩下一個相加,并賦值給其中某個變量,得到新的四個變量,重復16次這個過程,得到的四個變量作為幻數,與下一個分組進行相似的計算。
  • 遍歷所有分組后得到的四個變量即為結果。
  • 詳見:https://blog.csdn.net/weixin_39640298/article/details/84555814

    • 為什么不可逆:因為MD5在進行消息摘要的過程中,數據與原始數據相比發生了丟失,所以不能由結果進行恢復。

    • 加強安全性:加鹽(加隨機數)

    (33) 單條記錄高并發訪問的優化

    服務器端:

    • 使用緩存,如redis等
    • 使用分布式架構進行處理
    • 將靜態頁面和靜態資源存儲在靜態資源服務器,需要處理的數據使用服務器進行計算后返回
    • 將靜態資源盡可能在客戶端進行緩存
    • 采用ngnix進行負載均衡 (nginx讀作恩靜埃克斯 = Engine X)

    數據庫端:

    • 數據庫采用主從賦值,讀寫分離措施
    • 建立適當的索引
    • 分庫分表

    (34) 介紹一下ping的過程,分別用到了哪些協議 (百度安全等)

    詳見:Ping原理與ICMP協議

    ping是使用ICMP協議來進行工作的。 ICMP:網絡控制報文協議

    • 首先,ping命令會構建一個ICMP請求數據包,然后由ICMP協議將這個數據包連同目的IP地址源IP地址一起交給IP協議。
    • 然后IP協議就會構建一個IP數據報,并且在映射表中查找目的IP對應的mac地址,將其交給數據鏈路層。
    • 然后數據鏈路層就會構建一個數據幀,附上源mac地址和目的mac地址發送出去。

    目的主機接收到數據幀后,就會檢查包上的mac地址與本機mac是否相符,如果相符,就接收并把其中的信息提取出來交給IP協議,IP協議就會將其中的信息提取出來交給ICMP協議。然后構建一個ICMP應答包,用相同的過程發送回去。

    (35) TCP/IP的粘包與避免介紹一下

    因為TCP為了減少額外開銷,采取的是流式傳輸,所以接收端在一次接收的時候有可能一次接收多個包。而TCP粘包就是發送方的若干個數據包到達接收方的時候粘成了一個包。多個包首尾相接,無法區分。

    導致TCP粘包的原因有三方面:

    • 發送端等待緩沖區滿才進行發送,造成粘包
    • 接收方來不及接收緩沖區內的數據,造成粘包
    • 由于TCP協議在發送較小的數據包的時候,會將幾個包合成一個包后發送

    避免粘包的措施:

    • 通過編程,強制使TCP發生數據傳送,不必等到緩沖區滿
    • 優化接收方接收數據的過程,使其來得及接收數據包,包括提高接收進程優先級等
    • 設置固定長度的報文或者設置報文頭部指示報文的長度。

    (36) 說一下TCP的封包和拆包

    因為TCP是無邊界的流傳輸,所以需要對TCP進行封包和拆包,確保發送和接收的數據不粘連。

    • 封包:封包就是在發送數據報的時候為每個TCP數據包加上一個包頭,將數據報分為包頭和包體兩個部分。包頭是一個固定長度的結構體,里面包含該數據包的總長度。
    • 拆包:接收方在接收到報文后提取包頭中的長度信息進行截取。

    (37) 一個ip配置多個域名,靠什么識別?

    • 靠host主機名區分
    • 靠端口號區分

    (38) 服務器攻擊(DDos攻擊)

    (39)DNS的工作過程和原理


    DNS解析有兩種方式:遞歸查詢和迭代查詢

    • 遞歸查詢 用戶先向本地域名服務器查詢,如果本地域名服務器的緩存沒有IP地址映射記錄,就向根域名服務器查詢,根域名服務器就會向頂級域名服務器查詢,頂級域名服務器向權限域名服務器查詢,查到結果后依次返回。
    • 迭代查詢 用戶向本地域名服務器查詢,如果沒有緩存,本地域名服務器會向根域名服務器查詢,根域名服務器返回頂級域名服務器的地址,本地域名服務器再向頂級域名服務器查詢,得到權限域名服務器的地址,本地域名服務器再向權限域名服務器查詢得到結果

    (41)OSA七層協議和五層協議,分別有哪些

    OSI七層協議模型主要是:應用層(Application)、表示層(Presentation)、會話層(Session)、傳輸層(Transport)、網絡層(Network)、數據鏈路層(Data Link)、物理層(Physical)。

    五層體系結構包括:應用層、傳輸層、網絡層、數據鏈路層和物理層。

    (42)IP尋址和MAC尋址有什么不同,怎么實現的

    通過MAC地址尋找主機是MAC地址尋址,通過IP地址尋找主機叫IP地址尋址。它們適用于不同的協議層,IP尋址是網絡層,Mac尋址是數據鏈路層。

    http://c.biancheng.net/view/6388.html

    https://blog.csdn.net/wxy_nick/article/details/9190693

    IP尋址的過程(ARP協議):主機A想通過IP地址尋找到目標主機,首先分析IP地址確定目標主機與自己是否為同一網段。如果是則查看ARP緩存,或者使用ARP協議發送廣播。如果不是,則尋找網關發送ARP數據包

    3. 數據庫

    (1) 關系型和非關系型數據庫的區別(低頻)

    • 關系型數據庫的優點
    • 容易理解。因為它采用了關系模型來組織數據。
    • 可以保持數據的一致性。
    • 數據更新的開銷比較小。
    • 支持復雜查詢(帶where子句的查詢)
    • 非關系型數據庫的優點
    • 不需要經過sql層的解析,讀寫效率高。
    • 基于鍵值對,數據的擴展性很好。
    • 可以支持多種類型數據的存儲,如圖片,文檔等等。

    (2) 什么是非關系型數據庫(低頻)

    非關系型數據庫也叫nosql,采用鍵值對的形式進行存儲。它的讀寫性能很高,易于擴展。例如Redis,Mongodb,hbase等等。

    適合使用非關系型數據庫的場景:

    • 日志系統
    • 地理位置存儲
    • 數據量巨大
    • 高可用

    (3) 說一下 MySQL 執行一條查詢語句的內部執行過程?

    • 連接器:客戶端先通過連接器連接到 MySQL 服務器。
    • 緩存:連接器權限驗證通過之后,先查詢是否有查詢緩存,如果有緩存(之前執行過此語句)則直接返回緩存數據,如果沒有緩存則進入分析器。
    • 分析器:分析器會對查詢語句進行語法分析和詞法分析,判斷 SQL 語法是否正確,如果查詢語法錯誤會直接返回給客戶端錯誤信息,如果語法正確則進入優化器。
    • 優化器:優化器是對查詢語句進行優化處理,例如一個表里面有多個索引,優化器會判別哪個索引性能更好。
    • 執行器:優化器執行完就進入執行器,執行器就開始執行語句進行查詢比對了,直到查詢到滿足條件的所有數據,然后進行返回。

    (4) 數據庫的索引類型

    數據庫的索引類型分為邏輯分類和物理分類

    邏輯分類:

    • 主鍵索引 當關系表中定義主鍵時會自動創建主鍵索引。每張表中的主鍵索引只能有一個,要求主鍵中的每個值都唯一,即不可重復,也不能有空值。
    • 唯一索引 數據列不能有重復,可以有空值。一張表可以有多個唯一索引,但是每個唯一索引只能有一列。如身份證,卡號等。
    • 普通索引 一張表可以有多個普通索引,可以重復可以為空值
    • 全文索引 可以加快模糊查詢,不常用

    物理分類:

    • 聚集索引(聚簇索引) 數據在物理存儲中的順序跟索引中數據的邏輯順序相同,比如以ID建立聚集索引,數據庫中id從小到大排列,那么物理存儲中該數據的內存地址值也按照從小到大存儲。一般是表中的主鍵索引,如果沒有主鍵索引就會以第一個非空的唯一索引作為聚集索引。一張表只能有一個聚集索引。
    • 非聚集索引 數據在物理存儲中的順序跟索引中數據的邏輯順序不同。非聚集索引因為無法定位數據所在的行,所以需要掃描兩遍索引樹。第一遍掃描非聚集索引的索引樹,確定該數據的主鍵ID,然后到主鍵索引(聚集索引)中尋找相應的數據。

    (5) 說一下事務是怎么實現的

    https://blog.csdn.net/u013256816/article/details/103966510

    https://www.cnblogs.com/takumicx/p/9998844.html

    事務就是一組邏輯操作的集合。實現事務就是要保證可靠性和并發隔離,或者說,能夠滿足ACID特性的機制。而這些主要是靠日志恢復和并發控制實現的。

    • 日志恢復:數據庫里有兩個日志,一個是redo log,一個是undo log。redo log記錄的是已經成功提交的事務操作信息,用來恢復數據,保證事務的持久性。undo log記錄的是事務修改之前的數據信息,用來回滾數據,保證事務的原子性
    • 并發控制:并發控制主要靠讀寫鎖和MVCC(多版本并發控制)來實現。讀寫鎖包括共享鎖和排他鎖,保證事務的隔離性。MVCC通過為數據添加時間戳來實現。

    (6) MySQL怎么建立索引,怎么建立主鍵索引,怎么刪除索引?

    MySQL建立索引有兩種方式:用alter table或者create index。

    alter table table_name add primary key(column_list) #添加一個主鍵索引 alter table table_name add index (column_list) #添加一個普通索引 alter table table_name add unique (column_list) #添加一個唯一索引 create index index_name on table_name (column_list) #創建一個普通索引 create unique index_name on table_name (column_list) #創建一個唯一索引

    Mysql刪除索引同樣也有兩種方式:alter table 和 drop index

    alter table table_name drop index index_name #刪除一個普通索引 alter table table_name drop primary key #刪除一個主鍵索引 drop index index_name on table table_name

    (7) 索引的優缺點,什么時候使用索引,什么時候不能使用索引(重點)

    https://www.cnblogs.com/wezheng/p/8399305.html

    • 經常搜索的列上建索引
    • 作為主鍵的列上要建索引
    • 經常需要連接(where子句)的列上
    • 經常需要排序的列
    • 經常需要范圍查找的列

    哪些列不適合建索引?

    • 很少查詢的列
    • 更新很頻繁的列
    • 數據值的取值比較少的列(比如性別)

    (8) 索引的底層實現(重點)

    數據庫的索引是使用B+樹來實現的。

    (為什么要用B+樹,為什么不用紅黑樹和B樹)

    B+樹是一種特殊的平衡多路樹,是B樹的優化改進版本,它把所有的數據都存放在葉節點上,中間節點保存的是索引。這樣一來相對于B樹來說,減少了數據對中間節點的空間占用,使得中間節點可以存放更多的指針,使得樹變得更矮,深度更小,從而減少查詢的磁盤IO次數,提高查詢效率。另一個是由于葉節點之間有指針連接,所以可以進行范圍查詢,方便區間訪問。

    而紅黑樹是二叉的,它的深度相對B+樹來說更大,更大的深度意味著查找次數更多,更頻繁的磁盤IO,所以紅黑樹更適合在內存中進行查找。

    (9) B樹和B+樹的區別(重點)

    這都是由于B+樹和B具有不同的存儲結構所造成的區別,以一個m階樹為例。

  • 關鍵字的數量不同;B+樹中分支結點有m個關鍵字,其葉子結點也有m個,其關鍵字只是起到了一個索引的作用,但是B樹雖然也有m個子結點,但是其只擁有m-1個關鍵字。
  • 存儲的位置不同;B+樹中的數據都存儲在葉子結點上,也就是其所有葉子結點的數據組合起來就是完整的數據,但是B樹的數據存儲在每一個結點中,并不僅僅存儲在葉子結點上。
  • 分支結點的構造不同;B+樹的分支結點僅僅存儲著關鍵字信息和兒子的指針(這里的指針指的是磁盤塊的偏移量),也就是說內部結點僅僅包含著索引信息。
  • 查詢不同;B樹在找到具體的數值以后,則結束,而B+樹則需要通過索引找到葉子結點中的數據才結束,也就是說B+樹的搜索過程中走了一條從根結點到葉子結點的路徑。
  • B+樹優點:由于B+樹的數據都存儲在葉子結點中,分支結點均為索引,方便掃庫,只需要掃一遍葉子結點即可,但是B樹因為其分支結點同樣存儲著數據,我們要找到具體的數據,需要進行一次中序遍歷按序來掃,所以B+樹更加適合在區間查詢的情況,所以通常B+樹用于數據庫索引,而B樹則常用于文件索引。

    (10) 索引最左前綴/最左匹配

    假如我們對a b c三個字段建立了聯合索引,在聯合索引中,從最左邊的字段開始,任何連續的索引都能匹配上,當遇到范圍查詢的時候停止。比如對于聯合索引index(a,b,c),能匹配a,ab,abc三組索引。并且對查詢時字段的順序沒有限制,也就是a,b,c; b,a,c; c,a,b; c,b,a都可以匹配。

    (11) Mysql的優化(高頻,索引優化,性能優化)

    高頻訪問:

    • 分表分庫:將數據庫表進行水平拆分,減少表的長度
    • 增加緩存: 在web和DB之間加上一層緩存層
    • 增加數據庫的索引:在合適的字段加上索引,解決高頻訪問的問題

    并發優化:

    • 主從讀寫分離:只在主服務器上寫,從服務器上讀
    • 負載均衡集群:通過集群或者分布式的方式解決并發壓力

    (12) MYSQL數據庫引擎介紹,innodb和myisam的特點與區別

    • InnoDB : InnoDB是mysql的默認引擎,支持事務和外鍵,支持容災恢復。適合更新頻繁和多并發的表 行級鎖
    • MyISAM : 插入和查詢速度比較高,支持大文件,但是不支持事務,適合在web和數據倉庫場景下使用 表級鎖
    • MEMORY : memory將表中的數據保存在內存里,適合數據比較小而且頻繁訪問的場景
    • CSV
    • blackhole

    (13) 數據庫中事務的ACID(四大特性都要能夠舉例說明,理解透徹,比如原子性和一致性的關聯,隔離性不好會出現的問題)

    數據庫事務是指邏輯上對數據的一種操作,這個事務要么全部成功,要么全部失敗。

    A: atom 原子性

    數據庫事務的原子性是指:事務是一個不可分割的工作單位,這組操作要么全部發生,要么全部不發生。

    C: consistency 一致性

    數據庫事務的一致性是指:在事務開始以前,數據庫中的數據有一個一致的狀態。在事務完成后,數據庫中的事務也應該保持這種一致性。事務應該將數據從一個一致性狀態轉移到另一個一致性狀態。
    比如在銀行轉賬操作后兩個賬戶的總額應當不變。

    I: isolation 隔離性

    數據庫事務的隔離性要求數據庫中的事務不會受另一個并發執行的事務的影響,對于數據庫中同時執行的每個事務來說,其他事務要么還沒開始執行,要么已經執行結束,它都感覺不到還有別的事務正在執行。

    D:durability 持久性

    數據庫事務的持久性要求事務對數據庫的改變是永久的,哪怕數據庫發生損壞都不會影響到已發生的事務。
    如果事務沒有完成,數據庫因故斷電了,那么重啟后也應該是沒有執行事務的狀態,如果事務已經完成后數據庫斷電了,那么重啟后就應該是事務執行完成后的狀態。

    (14)什么是臟讀,不可重復讀和幻讀?

    詳見數據庫的事務隔離級別總結

    • 臟讀:臟讀是指一個事務在處理過程中讀取了另一個還沒提交的事務的數據。

      比如A向B轉賬100,A的賬戶減少了100,而B的賬戶還沒來得及修改,此時一個并發的事務訪問到了B的賬戶,就是臟讀

    • 不可重復讀:不可重復讀是對于數據庫中的某一個字段,一個事務多次查詢卻返回了不同的值,這是由于在查詢的間隔中,該字段被另一個事務修改并提交了。

      比如A第一次查詢自己的賬戶有1000元,此時另一個事務給A的賬戶增加了1000元,所以A再次讀取他的賬戶得到了2000的結果,跟第一次讀取的不一樣。
      不可重復讀與臟讀的不同之處在于,臟讀是讀取了另一個事務沒有提交的臟數據,不可重復讀是讀取了已經提交的數據,實際上并不是一個異常現象。

    • 幻讀:事務多次讀取同一個范圍的時候,查詢結果的記錄數不一樣,這是由于在查詢的間隔中,另一個事務新增或刪除了數據。

      比如A公司一共有100個人,第一次查詢總人數得到100條記錄,此時另一個事務新增了一個人,所以下一次查詢得到101條記錄。
      不可重復度和幻讀的不同之處在于,幻讀是多次讀取的結果行數不同,不可重復度是讀取結果的值不同。

    避免不可重復讀需要鎖行,避免幻讀則需要鎖表。

    臟讀,不可重復讀和幻讀都是數據庫的讀一致性問題,是在并行的過程中出現的問題,必須采用一定的隔離級別解決。
    詳見臟讀、不可重復讀和幻讀的區別

    (15) 數據庫的隔離級別,mysql和Oracle的隔離級別分別是什么(重點)

    詳見數據庫的事務隔離級別總結和數據庫隔離級別

    為了保證數據庫事務一致性,解決臟讀,不可重復讀和幻讀的問題,數據庫的隔離級別一共有四種隔離級別:

    • 讀未提交 Read Uncommitted: 最低級別的隔離,不能解決以上問題
    • 讀已提交 Read committed: 可以避免臟讀的發生
    • 可重復讀 Reapeatable read: 確保事務可以多次從一個字段中讀取相同的值,在該事務執行期間,禁止其他事務對此字段的更新,可以避免臟讀和不可重復讀。 通過鎖行來實現
    • 串行化 Serializaion 最嚴格的事務隔離機制,要求所有事務被串行執行,可以避免以上所有問題。 通過鎖表來實現

    Oracle的默認隔離級別是讀已提交,實現了四種隔離級別中的讀已提交和串行化隔離級別

    MySQL的默認隔離級別是可重復讀,并且實現了所有四種隔離級別

    (16) 數據庫連接池的作用

    (17) Mysql的表空間方式,各自特點

    • 共享表空間:指的是數據庫的所有的表數據,索引文件全部放在一個文件中,默認這個共享表空間的文件路徑在 data 目錄下。
    • 獨立表空間:每一個表都將會生成以獨立的文件方式來進行存儲。 優點:當表被刪除時這部分空間可以被回收;可以更快的恢復和備份單個表;將單個表復制到另一個實例會很方便; 缺點:mysqld會維持很多文件句柄,表太多會影響性能。如果很多表都增長會導致碎片問題

    (18) 分布式事務

    (19) 數據庫的范式

    https://www.cnblogs.com/linjiqin/archive/2012/04/01/2428695.html

    • 第一范式(確保每列保持原子性)

      第一范式是最基本的范式。如果數據庫表中的所有字段值都是不可分解的原子值,就說明該數據庫表滿足了第一范式。

    比如 學生 選課(包括很多課程) 就不符合第一范式

    • 第二范式(確保表中的每列都和主鍵相關)

      在滿足第一范式的前提下,(主要針對聯合主鍵而言)第二范式需要確保數據庫表中的每一列都和主鍵的所有成員直接相關,由整個主鍵才能唯一確定,而不能只與主鍵的某一部分相關或者不相關。

    比如一張學生信息表,由主鍵(學號)可以唯一確定一個學生的姓名,班級,年齡等信息。但是主鍵 (學號,班級) 與列 姓名,班主任,教室 就不符合第二范式,因為班主任跟部分主鍵(班級)是依賴關系

    • 第三范式(確保非主鍵的列沒有傳遞依賴)

      在滿足第二范式的前提下,第三范式需要確保數據表中的每一列數據都和主鍵直接相關,而不能間接相關。非主鍵的列不能確定其他列,列與列之間不能出現傳遞依賴。

    比如一張學生信息表,主鍵是(學號)列包括 姓名,班級,班主任 就不符合第三范式,因為非主鍵的列中 班主任 依賴于 班級

    • BCNF范式(確保主鍵之間沒有傳遞依賴)

      主鍵有可能是由多個屬性組合成的復合主鍵,那么多個主鍵之間不能有傳遞依賴。也就是復合主鍵之間誰也不能決定誰,相互之間沒有關系。

    (20) 數據的鎖的種類,加鎖的方式

    以MYSQL為例,

    • 按照類型來分有樂觀鎖和悲觀鎖
    • 根據粒度來分有行級鎖,頁級鎖,表級鎖(粒度一個比一個大) (僅BDB,Berkeley Database支持頁級鎖)
    • 根據作用來分有共享鎖(讀鎖)和排他鎖(寫鎖)。

    (21) 什么是共享鎖和排他鎖

    • 共享鎖是讀操作的時候創建的鎖,一個事務對數據加上共享鎖之后,其他事務只能對數據再加共享鎖,不能進行寫操作直到釋放所有共享鎖。

    • 排他鎖是寫操作時創建的鎖,事務對數據加上排他鎖之后其他任何事務都不能對數據加任何的鎖(即其他事務不能再訪問該數據)

      https://blog.csdn.net/qq_42743933/article/details/81236658

    (22) 分庫分表的理解和簡介

    (23)

    (24)數據庫高并發的解決方案

  • 在web服務框架中加入緩存。在服務器與數據庫層之間加入緩存層,將高頻訪問的數據存入緩存中,減少數據庫的讀取負擔。
  • 增加數據庫索引。提高查詢速度。(不過索引太多會導致速度變慢,并且數據庫的寫入會導致索引的更新,也會導致速度變慢)
  • 主從讀寫分離,讓主服務器負責寫,從服務器負責讀。
  • 將數據庫進行拆分,使得數據庫的表盡可能小,提高查詢的速度。
  • 使用分布式架構,分散計算壓力。
  • (25)樂觀鎖與悲觀鎖解釋一下

    一般的數據庫都會支持并發操作,在并發操作中為了避免數據沖突,所以需要對數據上鎖,樂觀鎖和悲觀鎖就是兩種不同的上鎖方式。

    悲觀鎖假設數據在并發操作中一定會發生沖突,所以在數據開始讀取的時候就把數據鎖住。而樂觀鎖則假設數據一般情況下不會發生沖突,所以在數據提交更新的時候,才會檢測數據是否有沖突。

    (26)樂觀鎖與悲觀鎖是怎么實現的

    悲觀鎖有行級鎖和頁級鎖兩種形式。行級鎖對正在使用的單條數據進行鎖定,事務完成后釋放該行數據,而頁級鎖則對整張表進行鎖定,事務正在對該表進行訪問的時候不允許其他事務并行訪問。

    悲觀鎖要求在整個過程中一直與數據庫有一條連接,因為上一個事務完成后才能讓下一個事務執行,這個過程是串行的。

    樂觀鎖有三種常用的實現形式:

    • 一種是在執行事務時把整個數據都拷貝到應用中,在數據更新提交的時候比較數據庫中的數據與新數據,如果兩個數據一摸一樣則表示沒有沖突可以直接提交,如果有沖突就要交給業務邏輯去解決。
    • 一種是使用版本戳來對數據進行標記,數據每發生一次修改,版本號就增加1。某條數據在提交的時候,如果數據庫中的版本號與自己的一致,就說明數據沒有發生修改,否則就認為是過期數據需要處理。
    • 最后一種采用時間戳對數據最后修改的時間進行標記。與上一種類似。

    (27)對數據庫目前最新技術有什么了解嗎

    4. Linux

    (1) Linux的I/O模型介紹以及同步異步阻塞非阻塞的區別(超級重要)

    https://blog.csdn.net/sqsltr/article/details/92762279

    https://www.cnblogs.com/euphie/p/6376508.html

    (IO過程包括兩個階段:(1)內核從IO設備讀寫數據和(2)進程從內核復制數據)

    • 阻塞:調用IO操作的時候,如果緩沖區空或者滿了,調用的進程或者線程就會處于阻塞狀態直到IO可用并完成數據拷貝。

    • 非阻塞:調用IO操作的時候,內核會馬上返回結果,如果IO不可用,會返回錯誤,這種方式下進程需要不斷輪詢直到IO可用為止,但是當進程從內核拷貝數據時是阻塞的。

    • IO多路復用就是同時監聽多個描述符,一旦某個描述符IO就緒(讀就緒或者寫就緒),就能夠通知進程進行相應的IO操作,否則就將進程阻塞在select或者epoll語句上。

    • 同步IO:同步IO模型包括阻塞IO,非阻塞IO和IO多路復用。特點就是當進程從內核復制數據的時候都是阻塞的。

    • 異步IO:在檢測IO是否可用和進程拷貝數據的兩個階段都是不阻塞的,進程可以做其他事情,當IO完成后內核會給進程發送一個信號。

    (2) 文件系統的理解(EXT4,XFS,BTRFS)

    (3) EPOLL的介紹和了解

    https://zhuanlan.zhihu.com/p/56486633

    https://www.jianshu.com/p/397449cadc9a

    https://blog.csdn.net/davidsguo008/article/details/73556811

    Epoll是Linux進行IO多路復用的一種方式,用于在一個線程里監聽多個IO源,在IO源可用的時候返回并進行操作。它的特點是基于事件驅動,性能很高。

    epoll將文件描述符拷貝到內核空間后使用紅黑樹進行維護,同時向內核注冊每個文件描述符的回調函數,當某個文件描述符可讀可寫的時候,將這個文件描述符加入到就緒鏈表里,并喚起進程,返回就緒鏈表到用戶空間,由用戶程序進行處理。

    Epoll有三個系統調用:epoll_create(),epoll_ctl()和epoll_wait()。

    • eoll_create()函數在內核中初始化一個eventpoll對象,同時初始化紅黑樹和就緒鏈表。

    • epoll_ctl()用來對監聽的文件描述符進行管理。將文件描述符插入紅黑樹,或者從紅黑樹中刪除,這個過程的時間復雜度是log(N)。同時向內核注冊文件描述符的回調函數。

    • epoll_wait()會將進程放到eventpoll的等待隊列中,將進程阻塞,當某個文件描述符IO可用時,內核通過回調函數將該文件描述符放到就緒鏈表里,epoll_wait()會將就緒鏈表里的文件描述符返回到用戶空間。

    (4) IO復用的三種方法(select,poll,epoll)深入理解,包括三者區別,內部原理實現?

    (1)select的方法介紹:select把所有監聽的文件描述符拷貝到內核中,掛起進程。當某個文件描述符可讀或可寫的時候,中斷程序喚起進程,select將監聽的文件描述符再次拷貝到用戶空間,然select后遍歷這些文件描述符找到IO可用的文件。下次監控的時候需要再次拷貝這些文件描述符到內核空間。select支持監聽的描述符最大數量是1024.

    (2)poll使用鏈表保存文件描述符,其他的跟select沒有什么不同。

    (3)epoll將文件描述符拷貝到內核空間后使用紅黑樹進行維護,同時向內核注冊每個文件描述符的回調函數,當某個文件描述符可讀可寫的時候,將這個文件描述符加入到就緒鏈表里,并喚起進程,返回就緒鏈表到用戶空間。

    詳見 https://www.cnblogs.com/Anker/p/3265058.html

    (5) Epoll的ET模式和LT模式(ET的非阻塞)

    • ET是邊緣觸發模式,在這種模式下,只有當描述符從未就緒變成就緒時,內核才會通過epoll進行通知。然后直到下一次變成就緒之前,不會再次重復通知。也就是說,如果一次就緒通知之后不對這個描述符進行IO操作導致它變成未就緒,內核也不會再次發送就緒通知。優點就是只通知一次,減少內核資源浪費,效率高。缺點就是不能保證數據的完整,有些數據來不及讀可能就會無法取出。
    • LT是水平觸發模式,在這個模式下,如果文件描述符IO就緒,內核就會進行通知,如果不對它進行IO操作,只要還有未操作的數據,內核都會一直進行通知。優點就是可以確保數據可以完整輸出。缺點就是由于內核會一直通知,會不停從內核空間切換到用戶空間,資源浪費嚴重。

    (6) 查詢進程占用CPU的命令(注意要了解到used,buf,代表意義)

    詳見:https://blog.csdn.net/qq_36357820/article/details/76606113

  • top命令查看linux負載:
  • uptime查看linux負載
  • w查看linux負載:
  • vmstat查看linux負載
  • (7) linux的其他常見命令(kill,find,cp等等)

    (8) shell腳本用法

    (9) 硬連接和軟連接的區別

    (10) 文件權限怎么看(rwx)

    (11) 文件的三種時間(mtime, atime,ctime),分別在什么時候會改變

    (12) Linux監控網絡帶寬的命令,查看特定進程的占用網絡資源情況命令

    (13)Linux中線程的同步方式有哪些?

    (14)怎么修改一個文件的權限

    chmod 777 (177 277 477 等,權限組合是 1 2 4,分別代表r x w )

    (15)查看文件內容常用命令

    詳見: http://blog.sina.com.cn/s/blog_7b4ce6b101018l8l.html

  • cat 與 tac
  • cat的功能是將文件從第一行開始連續的將內容輸出在屏幕上。當文件大,行數比較多時,屏幕無法全部容下時,只能看到一部分內容。所以通常使用重定向的方式,輸出滿足指定格式的內容cat語法:cat [-n] 文件名 (-n : 顯示時,連行號一起輸出)tac的功能是將文件從最后一行開始倒過來將內容數據輸出到屏幕上。我們可以發現,tac實際上是cat反過來寫。這個命令不常用。tac語法:tac 文件名。
  • more和less(常用)
  • more的功能是將文件從第一行開始,根據輸出窗口的大小,適當的輸出文件內容。當一頁無法全部輸出時,可以用“回車鍵”向下翻行,用“空格鍵”向下翻頁。退出查看頁面,請按“q”鍵。另外,more還可以配合管道符“|”(pipe)使用,例如:ls -al | moremore的語法:more 文件名Enter 向下n行,需要定義,默認為1行; Ctrl f 向下滾動一屏; 空格鍵 向下滾動一屏; Ctrl b 返回上一屏; = 輸出當前行的行號; :f 輸出文件名和當前行的行號; v 調用vi編輯器; ! 命令 調用Shell,并執行命令; q 退出moreless的功能和more相似,但是使用more無法向前翻頁,只能向后翻。less可以使用【pageup】和【pagedown】鍵進行前翻頁和后翻頁,這樣看起來更方便。less的語法:less 文件名
  • head和tail
  • head和tail通常使用在只需要讀取文件的前幾行或者后幾行的情況下使用。head的功能是顯示文件的前幾行內容head的語法:head [n number] 文件名 (number 顯示行數)tail的功能恰好和head相反,只顯示最后幾行內容tail的語法:tail [-n number] 文件名
  • nl
  • nl的功能和cat -n一樣,同樣是從第一行輸出全部內容,并且把行號顯示出來nl的語法:nl 文件名
  • vim
  • 這個用的太普遍了,主要是用于編輯。

    (16)怎么找出含有關鍵字的前后4行

    (17)Linux的GDB調試

    (18)coredump是什么 怎么才能coredump

    coredump是程序由于異常或者bug在運行時異常退出或者終止,在一定的條件下生成的一個叫做core的文件,這個core文件會記錄程序在運行時的內存,寄存器狀態,內存指針和函數堆棧信息等等。對這個文件進行分析可以定位到程序異常的時候對應的堆棧調用信息。

    coredump產生的條件

  • shell資源控制限制,使用 ulimit -c 命令查看shell執行程序時的資源 ,如果為0,則不會產生coredump。可以用ulimit -c unlimited設置為不限大小。
  • 讀寫越界,包括:數組訪問越界,指針指向錯誤的內存,字符串讀寫越界
  • 使用了線程不安全的函數,讀寫未加鎖保護
  • 錯誤使用指針轉換
  • 堆棧溢出
  • (19)tcpdump常用命令

    用簡單的話來定義tcpdump,就是:dump the traffic on a network,根據使用者的定義對網絡上的數據包進行截獲的包分析工具。 tcpdump可以將網絡中傳送的數據包的“頭”完全截獲下來提供分析。它支持針對網絡層、協議、主機、網絡或端口的過濾,并提供and、or、not等邏輯語句來幫助你去掉無用的信息。

    實用命令實例

    將某端口收發的數據包保存到文件

    sudo tcpdump -i any port 端口 -w 文件名.cap

    打印請求到屏幕

    sudo tcpdump -i any port 端口 -Xnlps0

    默認啟動

    tcpdump
    普通情況下,直接啟動tcpdump將監視第一個網絡接口上所有流過的數據包。
    監視指定網絡接口的數據包

    tcpdump -i eth1
    如果不指定網卡,默認tcpdump只會監視第一個網絡接口,一般是eth0,下面的例子都沒有指定網絡接口。

    (20) crontab命令

    詳見:https://www.cnblogs.com/peida/archive/2013/01/08/2850483.html

    corntab命令是用來指定用戶計劃任務的。用戶將需要定時執行的任務寫入crontab文件中,提交給crond進程定期執行。

    • crontab命令用來對crontab文件進行管理
    1.命令格式: crontab [-u user] file crontab [-u user] [ -e | -l | -r ] 2.命令功能: 通過crontab 命令,我們可以在固定的間隔時間執行指定的系統指令或 shell script腳本。時間間隔的單位可以是分鐘、小時、日、月、周及以上的任意組合。這個命令非常設合周期性的日志分析或數據備份等工作。 3.命令參數: -u user:用來設定某個用戶的crontab服務,例如,“-u ixdba”表示設定ixdba用戶的crontab服務,此參數一般有root用戶來運行。 file:file是命令文件的名字,表示將file做為crontab的任務列表文件并載入crontab。如果在命令行中沒有指定這個文件,crontab命令將接受標準輸入(鍵盤)上鍵入的命令,并將它們載入crontab。 -e:編輯某個用戶的crontab文件內容。如果不指定用戶,則表示編輯當前用戶的crontab文件。 -l:顯示某個用戶的crontab文件內容,如果不指定用戶,則表示顯示當前用戶的crontab文件內容。 -r:從/var/spool/cron目錄中刪除某個用戶的crontab文件,如果不指定用戶,則默認刪除當前用戶的crontab文件。 -i:在刪除用戶的crontab文件時給確認提示。
    • crontab文件內容

    crond是Linux下的周期性執行系統任務的守護進程,他會根據/etc下的crontab配置文件的內容執行。用戶需要將計劃任務寫入crontab文件中才能執行。

    用戶所建立的crontab文件中,每一行都代表一項任務,每行的每個字段代表一項設置,它的格式共分為六個字段,前五段是時間設定段,第六段是要執行的命令段,格式如下:

    minute hour day month week command其中: minute: 表示分鐘,可以是從0到59之間的任何整數。 hour:表示小時,可以是從0到23之間的任何整數。 day:表示日期,可以是從1到31之間的任何整數。 month:表示月份,可以是從1到12之間的任何整數。 week:表示星期幾,可以是從0到7之間的任何整數,這里的0或7代表星期日。 command:要執行的命令,可以是系統命令,也可以是自己編寫的腳本文件。 在以上各個字段中,還可以使用以下特殊字符: 星號(*):代表所有可能的值,例如month字段如果是星號,則表示在滿足其它字段的制約條件后每月都執行該命令操作。 逗號(,):可以用逗號隔開的值指定一個列表范圍,例如,“1,2,5,7,8,9” 中杠(-):可以用整數之間的中杠表示一個整數范圍,例如“2-6”表示“2,3,4,5,6” 正斜線(/):可以用正斜線指定時間的間隔頻率,例如“0-23/2”表示每兩小時執行一次。同時正斜線可以和星號一起使用,例如*/10,如果用在minute字段,表示每十分鐘執行一次。

    (21) 查看后臺進程

    • jobs

    查看當前控制臺的后臺進程

    想要停止后臺進程,使用jobs命令查看其進程號(比如為num),然后kill %num即可

    • ps

    查看后臺進程

    • top

    查看所有進程和資源使用情況,類似Windows中的任務管理器

    停止進程:界面是交互式的,在窗口輸入k 之后輸入PID,會提示輸入停止進程模式 有SIGTERM和 SIGKILL 如果留空不輸入,就是SIGTERM(優雅停止)

    退出top:輸入q即可

    5. 操作系統

    (1) 進程與線程的區別和聯系(重點)

    • 區別
  • 進程是對運行時程序的封裝,是系統進行資源分配和調度的基本單元,而線程是進程的子任務,是CPU分配和調度的基本單元。
  • 一個進程可以有多個線程,但是一個線程只能屬于一個進程。
  • 進程的創建需要系統分配內存和CPU,文件句柄等資源,銷毀時也要進行相應的回收,所以進程的管理開銷很大;但是線程的管理開銷則很小。
  • 進程之間不會相互影響;而一個線程崩潰會導致進程崩潰,從而影響同個進程里面的其他線程。
    • 聯系 進程與線程之間的關系:線程是存在進程的內部,一個進程中可以有多個線程,一個線程只能存在一個進程中。

    (2) Linux理論上最多可以創建多少個進程?一個進程可以創建多少線程,和什么有關

    答:32768. 因為進程的pid是用pid_t來表示的,pid_t的最大值是32768.所以理論上最多有32768個進程。

    至于線程。進程最多可以創建的線程數是根據分配給調用棧的大小,以及操作系統(32位和64位不同)共同決定的。Linux32位下是300多個。

    (3) 馮諾依曼結構有哪幾個模塊?分別對應現代計算機的哪幾個部分?(百度安全一面)

    • 存儲器:內存
    • 控制器:南橋北橋
    • 運算器:CPU
    • 輸入設備:鍵盤
    • 輸出設備:顯示器、網卡

    (4) 進程之間的通信方法有哪幾種 (重點)

    進程之間的通信方式主要有六種,包括管道,信號量,消息隊列,信號,共享內存,套接字

    • 管道:管道是半雙工的,雙方需要通信的時候,需要建立兩個管道。管道的實質是一個內核緩沖區,進程以先進先出的方式從緩沖區存取數據:管道一端的進程順序地將進程數據寫入緩沖區,另一端的進程則順序地讀取數據,該緩沖區可以看做一個循環隊列,讀和寫的位置都是自動增加的,一個數據只能被讀一次,讀出以后再緩沖區都不復存在了。當緩沖區讀空或者寫滿時,有一定的規則控制相應的讀進程或寫進程是否進入等待隊列,當空的緩沖區有新數據寫入或慢的緩沖區有數據讀出時,就喚醒等待隊列中的進程繼續讀寫。管道是最容易實現的

      匿名管道pipe和命名管道除了建立,打開,刪除的方式不同外,其余都是一樣的。匿名管道只允許有親緣關系的進程之間通信,也就是父子進程之間的通信,命名管道允許具有非親緣關系的進程間通信。

      管道的底層實現 https://segmentfault.com/a/1190000009528245

    • 信號量:信號量是一個計數器,可以用來控制多個進程對共享資源的訪問。信號量只有等待和發送兩種操作。等待(P(sv))就是將其值減一或者掛起進程,發送(V(sv))就是將其值加一或者將進程恢復運行。

    • 信號:信號是Linux系統中用于進程之間通信或操作的一種機制,信號可以在任何時候發送給某一進程,而無須知道該進程的狀態。如果該進程并未處于執行狀態,則該信號就由內核保存起來,知道該進程恢復執行并傳遞給他為止。如果一個信號被進程設置為阻塞,則該信號的傳遞被延遲,直到其阻塞被取消時才被傳遞給進程。 信號是開銷最小的

    • 共享內存:共享內存允許兩個或多個進程共享一個給定的存儲區,這一段存儲區可以被兩個或兩個以上的進程映射至自身的地址空間中,就像由malloc()分配的內存一樣使用。一個進程寫入共享內存的信息,可以被其他使用這個共享內存的進程,通過一個簡單的內存讀取讀出,從而實現了進程間的通信。共享內存的效率最高,缺點是沒有提供同步機制,需要使用鎖等其他機制進行同步。

    • 消息隊列:消息隊列就是一個消息的鏈表,是一系列保存在內核中消息的列表。用戶進程可以向消息隊列添加消息,也可以向消息隊列讀取消息。
      消息隊列與管道通信相比,其優勢是對每個消息指定特定的消息類型,接收的時候不需要按照隊列次序,而是可以根據自定義條件接收特定類型的消息。
      可以把消息看做一個記錄,具有特定的格式以及特定的優先級。對消息隊列有寫權限的進程可以向消息隊列中按照一定的規則添加新消息,對消息隊列有讀權限的進程可以從消息隊列中讀取消息。

    • 套接字:套接口也是一種進程間通信機制,與其他通信機制不同的是,它可用于不同設備及其間的進程通信。

    (5) 進程調度方法詳細介紹

    https://blog.csdn.net/u011080472/article/details/51217754

    https://blog.csdn.net/leex_brave/article/details/51638300

    • 先來先服務 (FCFS first come first serve):按照作業到達任務隊列的順序調度 FCFS是非搶占式的,易于實現,效率不高,性能不好,有利于長作業(CPU繁忙性)而不利于短作業(I/O繁忙性)。
    • 短作業優先 (SHF short job first):每次從隊列里選擇預計時間最短的作業運行。SJF是非搶占式的,優先照顧短作業,具有很好的性能,降低平均等待時間,提高吞吐量。但是不利于長作業,長作業可能一直處于等待狀態,出現饑餓現象;完全未考慮作業的優先緊迫程度,不能用于實時系統。
    • 最短剩余時間優先 該算法首先按照作業的服務時間挑選最短的作業運行,在該作業運行期間,一旦有新作業到達系統,并且該新作業的服務時間比當前運行作業的剩余服務時間短,則發生搶占;否則,當前作業繼續運行。該算法確保一旦新的短作業或短進程進入系統,能夠很快得到處理。
    • 高響應比優先調度算法(Highest Reponse Ratio First, HRRF)是非搶占式的,主要用于作業調度。基本思想:每次進行作業調度時,先計算后備作業隊列中每個作業的響應比,挑選最高的作業投入系統運行。響應比 = (等待時間 + 服務時間) / 服務時間 = 等待時間 / 服務時間 + 1。因為每次都需要計算響應比,所以比較耗費系統資源。
    • 時間片輪轉 用于分時系統的進程調度。基本思想:系統將CPU處理時間劃分為若干個時間片(q),進程按照到達先后順序排列。每次調度選擇隊首的進程,執行完1個時間片q后,計時器發出時鐘中斷請求,該進程移至隊尾。以后每次調度都是如此。該算法能在給定的時間內響應所有用戶的而請求,達到分時系統的目的。
    • 多級反饋隊列(Multilevel Feedback Queue)

    (6) 進程的執行過程是什么樣的,執行一個進程需要做哪些工作?

    進程的執行需要經過三大步驟:編譯,鏈接和裝入。

    • 編譯:將源代碼編譯成若干模塊
    • 鏈接:將編譯后的模塊和所需要的庫函數進行鏈接。鏈接包括三種形式:靜態鏈接,裝入時動態鏈接(將編譯后的模塊在鏈接時一邊鏈接一邊裝入),運行時動態鏈接(在執行時才把需要的模塊進行鏈接)
    • 裝入:將模塊裝入內存運行

    https://blog.csdn.net/qq_38623623/article/details/78306498

    將進程裝入內存時,通常使用分頁技術,將內存分成固定大小的頁,進程分為固定大小的塊,加載時將進程的塊裝入頁中,并使用頁表記錄。減少外部碎片。

    通常操作系統還會使用虛擬內存的技術將磁盤作為內存的擴充。

    (6) 操作系統的內存管理說一下

    https://www.cnblogs.com/peterYong/p/6556619.html

    https://zhuanlan.zhihu.com/p/141602175

    操作系統的內存管理包括物理內存管理和虛擬內存管理

    • 物理內存管理包括交換與覆蓋,分頁管理,分段管理和段頁式管理等;
    • 虛擬內存管理包括虛擬內存的概念,頁面置換算法,頁面分配策略等;

    (面試官這樣問的時候,其實是希望你能講講虛擬內存)

    (7) 實現一個LRU算法

    用到兩個數據結構:哈希+雙向鏈表

    unordered_map<int,list<pair<int,int> > > cache ;// 存放鍵,迭代器 list<pair<int,int>> auxlist; // 存放 <鍵,值> class LRUCache {int cap;list<pair<int,int>> l;// front:new back:old 存放值 新的放前面,因為前面的可以取得有效的迭代器map<int,list<pair<int,int> >::iterator > cache;// 存放鍵,迭代器 public:LRUCache(int capacity) {cap=capacity;}int get(int key) {auto mapitera = cache.find(key);if(mapitera==cache.end()){return -1;}else{// foundlist<pair<int,int>>::iterator listItera = mapitera->second;int value = (*listItera).second;l.erase(listItera);l.push_front({key,value});cache[key]=l.begin();return value;}}void put(int key, int value) {auto itera = cache.find(key);if(itera!=cache.end()){// existlist<pair<int,int>>::iterator listItera = itera->second;l.erase(listItera);l.push_front({key,value});cache[key]=l.begin();}else{// not existif(cache.size()>=cap){pair<int,int> oldpair = l.back();l.pop_back();cache.erase(oldpair.first);}l.push_front({key,value});cache[key]=l.begin();}} };/*** Your LRUCache object will be instantiated and called as such:* LRUCache* obj = new LRUCache(capacity);* int param_1 = obj->get(key);* obj->put(key,value);*/

    (8) 死鎖產生的必要條件(怎么檢測死鎖,解決死鎖問題)

    (1) 互斥:一個資源每次只能被一個進程使用。

    (2) 占有并請求:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。

    (3) 不可剝奪:進程已獲得的資源,在末使用完之前,不能強行剝奪。

    (4) 循環等待:若干進程之間形成一種頭尾相接的循環等待資源關系。

    產生死鎖的原因主要是:

    (1) 因為系統資源不足。

    (2) 進程運行推進的順序不合適。

    (3) 資源分配不當等。

    (9) 死鎖的恢復

  • 重新啟動:是最簡單、最常用的死鎖消除方法,但代價很大,因為在此之前所有進程已經完成的計算工作都將付之東流,不僅包括死鎖的全部進程,也包括未參與死鎖的全部進程。
  • 終止進程(process termination):終止參與死鎖的進程并回收它們所占資源。
    (1) 一次性全部終止;(2) 逐步終止(優先級,代價函數)
  • 剝奪資源(resource preemption):剝奪死鎖進程所占有的全部或者部分資源。
    (1) 逐步剝奪:一次剝奪死鎖進程所占有的一個或一組資源,如果死鎖尚未解除再繼續剝奪,直至死鎖解除為止。
    (2) 一次剝奪:一次性地剝奪死鎖進程所占有的全部資源。
  • 進程回退(rollback):讓參與死鎖的進程回退到以前沒有發生死鎖的某個點處,并由此點開始繼續執行,希望進程交叉執行時不再發生死鎖。但是系統開銷很大:
    (1) 要實現“回退”,必須“記住”以前某一點處的現場,而現場隨著進程推進而動態變化,需要花費大量時間和空間。
    (2) 一個回退的進程應當“挽回”它在回退點之間所造成的影響,如修改某一文件,給其它進程發送消息等,這些在實現時是難以做到的
  • (10)什么是饑餓

    饑餓是由于資源分配策略不公引起的,當進程或線程無法訪問它所需要的資源而不能繼續執行時,就會發生饑餓現象。

    (11) 如果要你實現一個mutex互斥鎖你要怎么實現?

    https://blog.csdn.net/kid551/article/details/84338619

    實現mutex最重要的就是實現它的lock()方法和unlock()方法。我們保存一個全局變量flag,flag=1表明該鎖已經鎖住,flag=0表明鎖沒有鎖住。
    實現lock()時,使用一個while循環不斷檢測flag是否等于1,如果等于1就一直循環。然后將flag設置為1;unlock()方法就將flag置為0;

    static int flag=0;void lock(){while(TestAndSet(&flag,1)==1);//flag=1; } void unlock(){flag=0; }

    因為while有可能被重入,所以可以用TestandSet()方法。

    int TestAndSet(int *ptr, int new) {int old = *ptr;*ptr = new;return old; }

    (12)線程之間的通信方式有哪些? 進程之間的同步方式又哪些?

    線程之間通信:

    • 使用全局變量
    • 使用信號機制
    • 使用事件

    進程之間同步:
    https://www.cnblogs.com/sonic4x/archive/2011/07/05/2098036.html

    • 信號量
    • 管程

    (13) 什么時候用多進程,什么時候用多線程

    https://blog.csdn.net/yu876876/article/details/82810178

    • 頻繁修改:需要頻繁創建和銷毀的優先使用多線程
    • 計算量:需要大量計算的優先使用多線程 因為需要消耗大量CPU資源且切換頻繁,所以多線程好一點
    • 相關性:任務間相關性比較強的用多線程,相關性比較弱的用多進程。因為線程之間的數據共享和同步比較簡單。
    • 多分布:可能要擴展到多機分布的用多進程,多核分布的用多線程

    但是實際中更常見的是進程加線程的結合方式,并不是非此即彼的。

    (14) 文件讀寫使用的系統調用

    (15) 孤兒進程和僵尸進程分別是什么,怎么形成的?

    https://www.cnblogs.com/Anker/p/3271773.html

    • 孤兒進程是父進程退出后它的子進程還在執行,這時候這些子進程就成為孤兒進程。孤兒進程會被init進程收養并完成狀態收集。
    • 僵尸進程是指子進程完成并退出后父進程沒有使用wait()或者waitpid()對它們進行狀態收集,這些子進程的進程描述符仍然會留在系統中。這些子進程就成為僵尸進程。

    (16) 說一下PCB/說一下進程地址空間/

    https://blog.csdn.net/qq_38499859/article/details/80057427

    PCB就是進程控制塊,是操作系統中的一種數據結構,用于表示進程狀態,操作系統通過PCB對進程進行管理。

    PCB中包含有:進程標識符,處理器狀態,進程調度信息,進程控制信息

    進程地址空間內有:

    • 代碼段text:存放程序的二進制代碼
    • 初始化的數據Data:已經初始化的變量和數據
    • 未初始化的數據BSS:還沒有初始化的數據

    (17) 內核空間和用戶空間是怎樣區分的

    在Linux中虛擬地址空間范圍為0到4G,最高的1G地址(0xC0000000到0xFFFFFFFF)供內核使用,稱為內核空間,低的3G空間(0x00000000到0xBFFFFFFF)供各個進程使用,就是用戶空間。

    內核空間中存放的是內核代碼和數據,而進程的用戶空間中存放的是用戶程序的代碼和數據。

    (18) 多線程是如何同步的(尤其是如果項目中用到了多線程,很大可能會結合討論)

    https://blog.csdn.net/s_lisheng/article/details/74278765

    • 臨界區
    • 信號量
    • 事件
    • 互斥量

    (19) 同一個進程內的線程會共享什么資源?

    • 該進程的地址空間
    • 全局變量
    • 堆空間

    線程的棧空間是自己獨有的

    (20) 異常和中斷的區別

    (21) 一般情況下在Linux/windows平臺下棧空間的大小

    在Linux下棧空間通常是8M,Windows下是1M

    (22)虛擬內存的了解

    https://www.cnblogs.com/Przz/p/6876988.html

    在運行一個進程的時候,它所需要的內存空間可能大于系統的物理內存容量。通常一個進程會有4G的空間,但是物理內存并沒有這么大,所以這些空間都是虛擬內存,它的地址都是邏輯地址,每次在訪問的時候都需要映射成物理地址。
    當進程訪問某個邏輯地址的時候,會去查看頁表,如果頁表中沒有相應的物理地址,說明內存中沒有這頁的數據,發生缺頁異常,這時候進程需要把數據從磁盤拷貝到物理內存中。如果物理內存已經滿了,就需要覆蓋已有的頁,如果這個頁曾經被修改過,那么還要把它寫回磁盤。

    (23)服務器高并發的解決方案

  • 應用數據與靜態資源分離
    將靜態資源(圖片,視頻,js,css等)單獨保存到專門的靜態資源服務器中,在客戶端訪問的時候從靜態資源服務器中返回靜態資源,從主服務器中返回應用數據。

  • 客戶端緩存
    因為效率最高,消耗資源最小的就是純靜態的html頁面,所以可以把網站上的頁面盡可能用靜態的來實現,在頁面過期或者有數據更新之后再將頁面重新緩存。或者先生成靜態頁面,然后用ajax異步請求獲取動態數據。

  • 集群和分布式
    (集群是所有的服務器都有相同的功能,請求哪臺都可以,主要起分流作用)

    (分布式是將不同的業務放到不同的服務器中,處理一個請求可能需要使用到多臺服務器,起到加快請求處理的速度。)

    可以使用服務器集群和分布式架構,使得原本屬于一個服務器的計算壓力分散到多個服務器上。同時加快請求處理的速度。

  • 反向代理
    在訪問服務器的時候,服務器通過別的服務器獲取資源或結果返回給客戶端。

  • (24)協程了解嗎(高頻)

    協程和微線程是一個東西。

    協程就是子程序在執行時中斷并轉去執行別的子程序,在適當的時候又返回來執行。
    這種子程序間的跳轉不是函數調用,也不是多線程執行,所以省去了線程切換的開銷,效率很高,并且不需要多線程間的鎖機制,不會發生變量寫沖突。

    (25)那協程的底層是怎么實現的,怎么使用協程?

    協程進行中斷跳轉時將函數的上下文存放在其他位置中,而不是存放在函數堆棧里,當處理完其他事情跳轉回來的時候,取回上下文繼續執行原來的函數。

    (26)進程的狀態以及轉換圖

    • 三態模型
      三態模型包括三種狀態:

    • 執行:進程分到CPU時間片,可以執行
    • 就緒:進程已經就緒,只要分配到CPU時間片,隨時可以執行
    • 阻塞:有IO事件或者等待其他資源
    • 五態模型

    • 新建態:進程剛剛創建。
    • 就緒態:
    • 運行態:
    • 等待態:出現等待事件
    • 終止態:進程結束
    • 七態模型

    • 新建態
    • 就緒掛起態
    • 就緒態
    • 運行態
    • 等待態
    • 掛起等待態
    • 終止態

    (27)在執行malloc申請內存的時候,操作系統是怎么做的?/內存分配的原理說一下/malloc函數底層是怎么實現的?/進程是怎么分配內存的?

    https://blog.csdn.net/yusiguyuan/article/details/39496057

    從操作系統層面上看,malloc是通過兩個系統調用來實現的: brk和mmap

    • brk是將進程數據段(.data)的最高地址指針向高處移動,這一步可以擴大進程在運行時的堆大小
    • mmap是在進程的虛擬地址空間中尋找一塊空閑的虛擬內存,這一步可以獲得一塊可以操作的堆內存。

    通常,分配的內存小于128k時,使用brk調用來獲得虛擬內存,大于128k時就使用mmap來獲得虛擬內存。

    進程先通過這兩個系統調用獲取或者擴大進程的虛擬內存,獲得相應的虛擬地址,在訪問這些虛擬地址的時候,通過缺頁中斷,讓內核分配相應的物理內存,這樣內存分配才算完成。

    (28)什么是字節序?怎么判斷是大端還是小端?有什么用?

    https://www.cnblogs.com/broglie/p/5645200.html

    字節序是對象在內存中存儲的方式,大端即為最高有效位在前面,小端即為最低有效位在前面。
    判斷大小端的方法:使用一個union數據結構

    union{short s;char c[2]; // sizeof(short)=2; }un; un.s=0x0102; if(un.c[0]==1 and un.c[1]==2) cout<<"大端"; if(un.c[0]==2 and un.c[1]==1) cout<<"小端";

    在網絡編程中不同字節序的機器發送和接收的順序不同。

    6. 場景題/算法題

    (0) leetcode hot100至少刷兩遍,劍指offer至少刷兩遍 重中之重!!

    面試中90%的算法題都從leetcode hot100和劍指offer中出 刷兩遍非常有必要

    (1) 介紹熟悉的設計模式(單例,簡單工廠模式)

    (2) 寫單例模式,線程安全版本

    // C++ version class Singleton{private:static Singleton* instance;Singleton(){// initialize}public:static Singleton* getInstance(){if(instance==nullptr) instance=new Singleton();return instance;} };

    (3) 寫三個線程交替打印ABC

    #include<iostream> #include<thread> #include<mutex> #include<condition_variable> using namespace std;mutex mymutex; condition_variable cv; int flag=0;void printa(){unique_lock<mutex> lk(mymutex);int count=0;while(count<10){while(flag!=0) cv.wait(lk);cout<<"thread 1: a"<<endl;flag=1;cv.notify_all();count++;}cout<<"my thread 1 finish"<<endl; } void printb(){unique_lock<mutex> lk(mymutex);for(int i=0;i<10;i++){while(flag!=1) cv.wait(lk);cout<<"thread 2: b"<<endl;flag=2;cv.notify_all();}cout<<"my thread 2 finish"<<endl; } void printc(){unique_lock<mutex> lk(mymutex);for(int i=0;i<10;i++){while(flag!=2) cv.wait(lk);cout<<"thread 3: c"<<endl;flag=0;cv.notify_all();}cout<<"my thread 3 finish"<<endl; } int main(){thread th2(printa);thread th1(printb);thread th3(printc);th1.join();th2.join();th3.join();cout<<" main thread "<<endl;}

    (4) 二維碼登錄的實現過程 場景題

    (5) 不使用臨時變量實現swap函數

    • 使用異或/加減等方式,下面給出使用異或的實現方法
    void swap(int& a,int& b){a=a^b;b=a^b;a=a^b; }

    (6) 實現一個strcpy函數(或者memcpy),如果內存可能重疊呢

    (7) 實現快排

    void swap(vector<int>& vec,int a,int b){vec[a]=vec[a]^vec[b];vec[b]=vec[a]^vec[b];vec[a]=vec[a]^vec[b]; } int partition(vector<int>& vec,int start,int end){int pivot=vec[start+(end-start)/2];while(start<end){while(start<end and vec[start]<pivot) start++;while(start<end and vec[end]>pivot) end--;if(start<end) swap(vec,start,end);}return start; } void quickSort(vector<int>& vec,int start,int end){if(start>end) return;int pivot=partition(vec,start,end);quickSort(vec,start,pivot-1);quickSort(vec,pivot+1,end); }

    (8) 實現一個堆排序

    堆排序的基本過程:

    • 將n個元素的序列構建一個大頂堆或小頂堆
    • 將堆頂的元素放到序列末尾
    • 將前n-1個元素重新構建大頂堆或小頂堆,重復這個過程,直到所有元素都已經排序

    整體時間復雜度為nlogn

    #include<iostream> #include<vector> using namespace std; void swap(vector<int>& arr, int a,int b){arr[a]=arr[a]^arr[b];arr[b]=arr[a]^arr[b];arr[a]=arr[a]^arr[b]; } void adjust(vector<int>& arr,int len,int index){int maxid=index;// 計算左右子節點的下標 left=2*i+1 right=2*i+2 parent=(i-1)/2int left=2*index+1,right=2*index+2;// 尋找當前以index為根的子樹中最大/最小的元素的下標if(left<len and arr[left]<arr[maxid]) maxid=left;if(right<len and arr[right]<arr[maxid]) maxid=right;// 進行交換,記得要遞歸進行adjust,傳入的index是maxidif(maxid!=index){swap(arr,maxid,index);adjust(arr,len,maxid);} } void heapsort(vector<int>&arr,int len){// 初次構建堆,i要從最后一個非葉子節點開始,所以是(len-1-1)/2,0這個位置要加等號for(int i=(len-1-1)/2;i>=0;i--){adjust(arr,len,i);}// 從最后一個元素的下標開始往前遍歷,每次將堆頂元素交換至當前位置,并且縮小長度(i為長度),從0處開始adjustfor(int i=len-1;i>0;i--){swap(arr,0,i);adjust(arr,i,0);// 注意每次adjust是從根往下調整,所以這里index是0!} } int main(){vector<int> arr={3,4,2,1,5,8,7,6};cout<<"before: "<<endl;for(int item:arr) cout<<item<<" ";cout<<endl;heapsort(arr,arr.size());cout<<"after: "<<endl;for(int item:arr)cout<<item<<" ";cout<<endl;return 0; }

    (8) 實現一個插入排序

    https://blog.csdn.net/left_la/article/details/8656425

    void insertSort(vector<int>& nums){int len=nums.size();for(int i=1;i<len;i++){int key=nums[i];int j=i-1;while(j>=0 and nums[j]>key){nums[j+1]=nums[j];j--;}nums[j+1]=key;} }

    (9) 快排存在的問題,如何優化

    • 3 種快排基準選擇方法:

    隨機(rand函數)、固定(隊首、隊尾)、三數取中(隊首、隊中和隊尾的中間數)

    • 4種優化方式:

    優化1:當待排序序列的長度分割到一定大小后,使用插入排序

    優化2:在一次分割結束后,可以把與Key相等的元素聚在一起,繼續下次分割時,不用再對與key相等元素分割

    優化3:優化遞歸操作

    優化4:使用并行或多線程處理子序列

    (10) 反轉一個鏈表(招銀網絡二面)

    ListNode* reverse(ListNode* root){ListNode* pre=nullptr,cur=root,nxt;while(cur!=nullptr){nxt=cur->next;cur->next=pre;pre=cur;cur=nxt;}return pre; }

    (11) Top K問題(可以采取的方法有哪些,各自優點?)(重點)

    Top K 問題的常見形式:

    給定10000個整數,找第K大(第K小)的數

    給定10000個整數,找出最大(最小)的前K個數

    給定100000個單詞,求前K詞頻的單詞

    解決Top K問題若干種方法

    • 使用最大最小堆。求最大的數用最小堆,求最小的數用最大堆。
    • Quick Select算法。使用類似快排的思路,根據pivot劃分數組。
    • 使用排序方法,排序后再尋找top K元素。
    • 使用選擇排序的思想,對前K個元素部分排序。
    • 將1000…個數分成m組,每組尋找top K個數,得到m×K個數,在這m×k個數里面找top K個數。
  • 使用最大最小堆的思路 (以top K 最大元素為例)

    按順序掃描這10000個數,先取出K個元素構建一個大小為K的最小堆。每掃描到一個元素,如果這個元素大于堆頂的元素(這個堆最小的一個數),就放入堆中,并刪除堆頂的元素,同時整理堆。如果這個元素小于堆頂的元素,就直接pass。最后堆中剩下的元素就是最大的前Top K個元素,最右的葉節點就是Top 第K大的元素。
  • note:最小堆的插入時間復雜度為log(n),n為堆中元素個數,在這里是K。最小堆的初始化時間復雜度是nlog(n)

    C++中的最大最小堆要用標準庫的priority_queue來實現。

    struct Node {int value;int idx;Node (int v, int i): value(v), idx(i) {}friend bool operator < (const struct Node &n1, const struct Node &n2) ; };inline bool operator < (const struct Node &n1, const struct Node &n2) {return n1.value < n2.value; }priority_queue<Node> pq; // 此時pq為最大堆
  • 使用Quick Select的思路(以尋找第K大的元素為例)

    Quick Select脫胎于快速排序,提出這兩個算法的都是同一個人。算法的過程是這樣的:
    首先選取一個樞軸,然后將數組中小于該樞軸的數放到左邊,大于該樞軸的數放到右邊。
    此時,如果左邊的數組中的元素個數大于等于K,則第K大的數肯定在左邊數組中,繼續對左邊數組執行相同操作;
    如果左邊的數組元素個數等于K-1,則第K大的數就是pivot;
    如果左邊的數組元素個數小于K,則第K大的數肯定在右邊數組中,對右邊數組執行相同操作。
  • 這個算法與快排最大的區別是,每次劃分后只處理左半邊或者右半邊,而快排在劃分后對左右半邊都繼續排序。

    //此為Java實現 public int findKthLargest(int[] nums, int k) {return quickSelect(nums, k, 0, nums.length - 1); }// quick select to find the kth-largest element public int quickSelect(int[] arr, int k, int left, int right) {if (left == right) return arr[right];int index = partition(arr, left, right);if (index - left + 1 > k)return quickSelect(arr, k, left, index - 1);else if (index - left + 1 == k)return arr[index];elsereturn quickSelect(arr, k - (index - left + 1), index + 1, right);}
  • 使用選擇排序的思想對前K個元素排序 ( 以尋找前K大個元素為例)

    掃描一遍數組,選出最大的一個元素,然后再掃描一遍數組,找出第二大的元素,再掃描一遍數組,找出第三大的元素。。。。。以此類推,找K個元素,時間復雜度為O(N*K)
  • (12) 8G的int型數據,計算機的內存只有2G,怎么對它進行排序?(外部排序)(百度一面)

    我們可以使用外部排序來對它進行處理。首先將整個文件分成許多份,比如說m份,劃分的依據就是使得每一份的大小都能放到內存里。然后我們用快速排序或者堆排序等方法對每一份數據進行一個內部排序,變成有序子串。接著對這m份有序子串進行m路歸并排序。取這m份數據的最小元素,進行排序,輸出排序后最小的元素到結果中,同時從該元素所在子串中讀入一個元素,直到所有數據都被輸出到結果中為止。

    https://blog.csdn.net/ailunlee/article/details/84548950

    (13) 自己構建一棵二叉樹,使用帶有null標記的前序遍歷序列

    在寫二叉樹相關算法的時候,如果需要自己構造測試用例(自己構造一棵二叉樹),往往是一件很麻煩的事情,我們可以用一個帶有null標記的前序遍歷序列來進行構造。 需要注意的是vec2tree()參數中的start是引用傳遞,而不是簡單的參數值傳遞

    #include<iostream> #include<vector> #include<queue> using namespace std;struct treeNode{string val;treeNode* left,*right;treeNode(string val):val(val){left=nullptr;right=nullptr;} };treeNode* vec2tree(vector<string>& vec,int& start){treeNode* root;if(vec[start]=="null"){start+=1;root=nullptr;}else{root=new treeNode(vec[start]);start+=1;root->left=vec2tree(vec,start);root->right=vec2tree(vec,start);}return root; }void tree2vec(treeNode *root,vector<string>& vec){if(root==nullptr){vec.push_back("null");}else{vec.push_back(root->val);tree2vec(root->left,vec);tree2vec(root->right,vec);} }int main(){vector<string> vec={"2","4","5","7","null","null","null","null","3","6","null","null","2","null","null"};int index=0,&start=index;treeNode* root=vec2tree(vec,start);//displaytree(root);vector<string> mvec;tree2vec(root,mvec);for(string item:mvec) cout<<item<<" ";cout<<endl;return 0;

    (14) 介紹一下b樹和它的應用場景有哪些

    B樹也叫做B-樹,或者平衡多路樹,它是每個節點最多有m個子樹的平衡樹。一個m階的B樹具有如下幾個特征:

  • 根結點至少有兩個子女。
  • 每個中間節點都包含至多m個子樹 , 每個節點包含的元素個數是其子樹個數-1(其中 m/2 <= k <= m)
  • 所有的葉子結點都位于同一層。
  • 每個節點中的元素從小到大排列,節點當中k-1個元素正好是k個子樹包含的元素的值域分劃。
  • b樹主要應用于文件系統中,在數據庫中(mongoDB)也有應用,與B+樹相比好處應該是有時不需要訪問到葉節點就可以獲取數據。

    查詢時間復雜度是logN

    (15) 介紹一下b+樹和它的應用場景有哪些

    B+樹是一種特殊的B樹,它把數據都存儲在葉子節點,并且葉節點間有指針連接。內部只存關鍵字(其中葉子節點的最小值作為索引)和孩子指針,簡化了內部節點。

    應用場景主要是數據庫的索引

    查詢時間復雜度也是logN
    https://zhuanlan.zhihu.com/p/110202102

    https://blog.csdn.net/hguisu/article/details/7786014

    (16) 介紹一下紅黑樹和它的應用場景有哪些

    紅黑樹是一種特殊的二叉查找樹,它在每一個節點上都使用紅色或黑色進行標記,通過一些性質確保它是始終平衡的。
    它的性質是這樣的:

  • 每個節點不是紅色就是黑色。
  • 根節點是黑色的。
  • 葉節點的空節點是黑色的。
  • 如果一個節點是紅色的,那么它的兩個子節點是黑色的。
  • 對于任意節點,從它到葉節點的每條路徑上都有相同數目的黑色節點。
  • 紅黑樹的插入,查詢,刪除在一般情況和最壞情況下的時間復雜度都是O(log(n))

    應用場景主要是STL中map,set的實現,優點在于支持頻繁的修改,因為查詢刪除插入時間復雜度都是logN

    (17) 怎么寫sql取表的前1000行數據(招銀網絡二面)

    select * limit 1000 from t1

    (18) N個骰子出現和為m的概率

    (19) 海量數據問題(可參考左神的書)

    (20) 一致性哈希

    (21)希爾排序說一下/手撕

    https://www.cnblogs.com/chengxiao/p/6104371.html
    希爾排序是把記錄按下標的一定增量分組,對每組使用直接插入排序算法排序;隨著增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個文件恰被分成一組,算法便終止。

    (22)Dijkstra算法說一下

    (23)實現一個動態數組要怎么實現,說思路(騰訊teg一面)

    模擬STL中vector的實現即可,去看一下vector的源碼。

    (24)最小生成樹算法說一下

    (25) 海量數據的bitmap使用原理

    bitmap算法就是使用一個比特映射一個值,它可以用在整數排序和數據壓縮上,因為使用一個比特位去存儲一個數,所以它可以大大節省空間。

    它的具體過程是:先根據數組中元素最大的數N計算需要分配多大的空間。
    如果使用int型數組的形式來保存的話,一個int = 4字節 =4*8比特 = 32比特。也就是一個int數可以映射32個數據(圖1),然后需要找到最大的數Max,表示最多需要的位數,所以需要開辟的數組空間為int a[1+Max/32]。
    然后需要推導一個整數a內如何映射32個數據,方法是將待存儲的數據模32,然后將a中相應位置的比特置為1。
    依此方法映射每一個元素,待讀取的時候掃描每個比特位,遇到值為1的就還原該數字。

    移位計算公式:
    N/32就是將N的二進制右移log32(也就是5)位 : N>>5

    N%32就是求N的后5位:N& 0x1F (0x1F = 00011111)

    模32然后相應位置置為1: a[i] |= 1<< N & 0x1F

    所以總的公式為: a[ N>>5 ] |= 1<< N & 0x1F

    BitMap算法評價

    • 優點:
    • 運算效率高,不進行比較和移位;
    • 占用內存少,比如最大的數MAX=10000000;只需占用內存為MAX/8=1250000Byte=1.25M。
    • 缺點:
    • 所有的數據不能重復,即不可對重復的數據進行排序。(少量重復數據查找還是可以的,用2-bitmap)。
    • 所需要的空間隨著最大元素的增大而增大,當數據類似(1,1000,10萬)只有3個數據的時候,用bitmap時間復雜度和空間復雜度相當大,只有當數據比較密集時才有優勢。

    (26) 布隆過濾器原理與優點

    布隆過濾器是一個比特向量或者比特數組,它本質上是一種概率型數據結構,用來查找一個元素是否在集合中,支持高效插入和查詢某條記錄。常作為針對超大數據量下高效查找數據的一種方法。

    它的具體工作過程是這樣子的:
    假設布隆過濾器的大小為m(比特向量的長度為m),有k個哈希函數,它對每個數據用這k個哈希函數計算哈希,得到k個哈希值,然后將向量中相應的位設為1。在查詢某個數據是否存在的時候,對這個數據用k個哈希函數得到k個哈希值,再在比特向量中相應的位查找是否為1,如果某一個相應的位不為1,那這個數據就肯定不存在。但是如果全找到了,則這個數據有可能存在。

    為什么說有可能存在呢?
    因為不同的數據經過哈希后可能有相同的哈希值,在比特向量上某個位置查找到1也可能是由于某個另外的數據映射得到的。

    支持刪除操作嗎
    目前布隆過濾器只支持插入和查找操作,不支持刪除操作,如果要支持刪除,就要另外使用一個計數變量,每次將相應的位置為1則計數加一,刪除則減一。

    布隆過濾器中哈希函數的個數需要選擇。如果太多則很快所有位都置為1,如果太少會容易誤報。

    布隆過濾器的大小以及哈希函數的個數怎么選擇?
    k 為哈希函數個數,m 為布隆過濾器長度,n 為插入的元素個數,p 為誤報率

    (27) 布隆過濾器處理大規模問題時的持久化,包括內存大小受限、磁盤換入換出問題

    (28)實現一個隊列,并且使它支持多線程,隊列有什么應用場景(阿里三面)

    //評測題目: class FIFOQueue { vector<int> vec(initCap,0); int start=0,end=0; condition_variable cv; mutex m; bool flag=false;// isFullbool enqueue(int v) {unique_lock<mutex> lk(m);while(flag==true) cv.wait(lk);end=(end+1)%initCap;vec[end]=v;cv.notifyall();return true;}}int dequeue() {unique_lock<mutex> lk(m);if(start!=end){int val = vec[start];start=(start+1)%initCap;flag=false;cv.notifyall();return val;}else{flag=false;cv.notifyall();return -1;}} }

    以上代碼是面試時寫的,并沒有運行,也許有錯誤,請客觀參考

    7. 智力題

    (1) 100層樓,只有2個雞蛋,想要判斷出那一層剛好讓雞蛋碎掉,給出策略(滴滴筆試中兩個鐵球跟這個是一類題)

    • (給定了樓層數和雞蛋數的情況)二分法+線性查找 從100/2=50樓扔起,如果破了就用另一個從0扔起直到破。如果沒破就從50/2=25樓扔起,重復。
    • 動態規劃

    (2) 毒藥問題,1000瓶水,其中有一瓶可以無限稀釋的毒藥,要快速找出哪一瓶有毒,需要幾只小白鼠

    用二進制的思路解決問題。2的十次方是1024,使用十只小鼠喝一次即可。方法是先將每瓶水編號,同時10個小鼠分別表示二進制中的一個位。將每瓶水混合到水瓶編號中二進制為1的小鼠對應的水中。喝完后統計,將死亡小鼠對應的位置為1,沒死的置為0,根據死亡小鼠的編號確定有毒的是哪瓶水,如0000001010表示10號水有毒。

    (3)

    (4) 先手必勝策略問題:100本書,每次能夠拿1-5本,怎么拿能保證最后一次是你拿

    尋找每個回合固定的拿取模式。最后一次是我拿,那么上個回合最少剩下6本。那么只要保持每個回合結束后都剩下6的倍數,并且在這個回合中我拿的和對方拿的加起來為6(這樣這個回合結束后剩下的還是6的倍數),就必勝。關鍵是第一次我必須先手拿(100%6=4)本(這不算在第一回合里面)。

    (5) 放n只螞蟻在一條樹枝上,螞蟻與螞蟻之間碰到就各自往反方向走,問總距離或者時間。

    碰到就當沒發生,繼續走,相當于碰到的兩個螞蟻交換了一下身體。其實就是每個螞蟻從當前位置一直走直到停止的總距離或者時間。

    (6) 瓶子換飲料問題:1000瓶飲料,3個空瓶子能夠換1瓶飲料,問最多能喝幾瓶

    拿走3瓶,換回1瓶,相當于減少2瓶。但是最后剩下4瓶的時候例外,這時只能換1瓶。所以我們計算1000減2能減多少次,直到剩下4.(1000-4=996,996/2=498)所以1000減2能減498次直到剩下4瓶,最后剩下的4瓶還可以換一瓶,所以總共是1000+498+1=1499瓶。

    (7)在24小時里面時針分針秒針可以重合幾次

    24小時中時針走2圈,而分針走24圈,時針和分針重合24-2=22次,而只要時針和分針重合,秒針一定有機會重合,所以總共重合22次

    (8) 有一個天平,九個砝碼,一個輕一些,用天平至少幾次能找到輕的?

    至少2次:第一次,一邊3個,哪邊輕就在哪邊,一樣重就是剩余的3個;
    第二次,一邊1個,哪邊輕就是哪個,一樣重就是剩余的那個;

    (9) 有十組砝碼每組十個,每個砝碼重10g,其中一組每個只有9g,有能顯示克數的秤最少幾次能找到輕的那一組砝碼?

    砝碼分組1~10,第一組拿一個,第二組拿兩個以此類推。。第十組拿十個放到秤上稱出克數x,則y = 550 - x,第y組就是輕的那組

    (10)生成隨機數問題:給定生成1到5的隨機數Rand5(),如何得到生成1到7的隨機數函數Rand7()?

    思路:由大的生成小的容易,比如由Rand7()生成Rand5(),所以我們先構造一個大于7的隨機數生成函數。
    記住下面這個式子:

    RandNN= N( RandN()-1 ) + RandN() ;// 生成1到N^2之間的隨機數
    可以看作是在數軸上撒豆子。N是跨度/步長,是RandN()生成的數的范圍長度,RandN()-1的目的是生成0到N-1的數,是跳數。后面+RandN()的目的是填滿中間的空隙

    比如Rand25= 5( Rand5()-1 ) + Rand5()可以生成1到25之間的隨機數。我們可以只要1到21(3*7)之間的數字,所以可以這么寫

    int rand7(){int x=INT_MAX;while(x>21){x=5*(rand5()-1)+rand5();}return x%7+1; }

    (11) 賽馬:有25匹馬,每場比賽只能賽5匹,至少要賽多少場才能找到最快的3匹馬?

    • 第一次,分成5個賽道ABCDE,每個賽道5匹馬,每個賽道比賽一場,每個賽道的第12345名記為 A1,A2,A3,A4,A5 B1,B2,B3,B4,B5等等,這一步要賽5場。
    • 第二次,我們將每個賽道的前三名,共15匹。分成三組,然后每組進行比賽。這一步要賽3場。
    • 第三次,我們取每組的前三名。共9匹,第一名賽道的馬編號為1a,1b,1c,第二名賽道的馬編號為2a,2b,2c,第三名賽道的馬編號為3a,3b,3c。這時進行分析,1a表示第一名里面的第一名,絕對是所有馬中的第一,所以不用再比了。2c表示第二名的三匹里頭的最后一匹,3b和3c表示第三名里面的倒數兩匹,不可能是所有馬里面的前三名,所以也直接排除,剩下1b,1c,2a,2b,3a,共5匹,再賽跑一次取第一第二名,加上剛篩選出來的1a就是所有馬里面的最快3匹了。這一步要賽1場。
    • 所以一共是5+3+1=9場。

    (12) 燒 香/繩子/其他 確定時間問題:有兩根不均勻的香,燃燒完都需要一個小時,問怎么確定15分鐘的時長?

    (說了求15分鐘,沒說開始的15分鐘還是結束的15分鐘,這里是可以求最后的15分鐘)點燃一根A,同時點燃另一根B的兩端,當另一根B燒完的時候就是半小時,這是再將A的另一端也點燃,從這時到A燃燒完就正好15分鐘。

    (13) 掰巧克力問題 NM塊巧克力,每次掰一塊的一行或一列,掰成11的巧克力需要多少次?(1000個人參加辯論賽,1V1,輸了就退出,需要安排多少場比賽)(快手提前批)

    每次拿起一塊巧克力,掰一下(無論橫著還是豎著)都會變成兩塊,因為所有的巧克力共有N*M塊,所以要掰N*M-1次,減1是因為最開始的一塊是不用算進去的。

    每一場辯論賽參加兩個人,淘汰一個人,所以可以看作是每一場辯論賽減少一個人,直到最后剩下1個人,所以是1000-1=999場。

    8. 大數據

    1. 介紹一下Hadoop

    Hadoop是一套大數據解決方案,提供了一套分布式的系統基礎架構,包括HDFS,MapReduce和YARN。

    • HDFS提供分布式的數據存儲
    • MapReduce負責進行數據運算
    • YARN負責任務調度

    HDFS是主從架構的,包括namenode,secondarynamenode和datanode。datanode負責存儲數據,namenode負責管理HDFS的目錄樹和文件元信息。

    MapReduce包括jobtracker,tasktracker和client。Jobtracker負責進行資源調度和作業監控。tasktracker會周期性的通過心跳向jobtracker匯報資源使用情況。

    2. 說一下MapReduce的運行機制

    MapReduce包括輸入分片、map階段、combine階段、shuffle階段和reduce階段。分布式計算框架包括client,jobtracker和tasktracker和調度器。

    • 輸入分片階段,mapreduce會根據輸入文件計算分片,每個分片對應一個map任務
    • map階段會根據mapper方法的業務邏輯進行計算,映射成鍵值對
    • combine階段是在節點本機進行一個reduce,減少傳輸結果對帶寬的占用
    • shuffle階段是對map階段的結果進行分區,排序,溢出然后寫入磁盤。將map端輸出的無規則的數據整理成為有一定規則的數據,方便reduce端進行處理,有點像洗牌的逆過程。 https://blog.csdn.net/ASN_forever/article/details/81233547
    • reduce階段是根據reducer方法的業務邏輯進行計算,最終結果會存在hdfs上。

    3. 介紹一下kafka

    https://blog.csdn.net/qq_29186199/article/details/80827085

    https://blog.csdn.net/student__software/article/details/81486431

    kafka是一個分布式消息隊列,包括producer、broker和consumer。kafka會對每個消息根據topic進行歸類,每個topic又會分成多個partition,消息會根據先進先出的方式存儲。消費者通過offset進行消費。

    kafka的特點是吞吐量高,可以進行持久化,高可用。

    4. 為什么kafka吞吐量高?/介紹一下零拷貝

    kafka吞吐量高是因為一個利用了磁盤順序讀寫的特性,速度比隨機讀寫要快很多,另一個是使用了零拷貝,數據直接在內核進行輸入和輸出,減少了用戶空間和內核空間的切換。

    零拷貝:傳統文件讀取并發送至網絡的步驟是:先將文件從磁盤拷貝到內核空間,然后內核空間拷貝到用戶空間的緩沖區,再從用戶空間拷貝到內核空間的socket緩沖區,最后拷貝到網卡并發送。而零拷貝技術是先將文件從磁盤空間拷貝到內核緩沖區,然后直接拷貝至網卡進行發送,減少了重復拷貝操作。

    5. 介紹一下spark

    https://blog.csdn.net/u011204847/article/details/51010205

    spark是一個通用內存并行計算框架。它可以在內存中對數據進行計算,效率很高,spark的數據被抽象成RDD(彈性分布式數據集)并且擁有DAG執行引擎,兼容性和通用性很好。可以和Hadoop協同工作。

    6. 介紹一下spark-streaming

    https://blog.csdn.net/yu0_zhang0/article/details/80569946

    spark-streaming是spark的核心組件之一。主要提供高效的流計算能力。spark-streaming的原理是將輸入數據流以時間片進行拆分,然后經過spark引擎以類似批處理的方式處理每個時間片數據。

    spark-streaming將輸入根據時間片劃分成一段一段的Dstream(也就是離散數據流),然后將每一段數據轉換成RDD進行操作。

    7. spark的transformation和action有什么區別

    spark的算子分成transformation和action兩類

    • transformation是變換算子,這類算子不會觸發提交,是延遲執行的。也就是說執行到transformation算子的時候數據并沒有馬上進行計算,只是記住了對RDD的邏輯操作
    • action算子是執行算子,會出發spark提交作業,并將數據輸出到spark

    8. spark常用的算子說幾個

    spark的算子分為兩類:transformation和action

    常用的transformation算子:

    // union 求并集 val rdd8 = rdd6.union(rdd7)// intersection 求交集 val rdd9 = rdd6.intersection(rdd7)// join 將rdd進行聚合連接,類似數據庫的join val rdd3 = rdd1.join(rdd2)// map flatMap mapPartition 傳入一個函數對數據集中的每一個數據進行操作 val arr1 = Array(1,2,3,4,5) val arr2 = rdd1.map(_+1)// countByKey reduceByKey partitionByKey 統計每個key有多少個鍵值對

    常用的action算子

    // reduce 按照一定的方法將元素進行合并 val rdd2 = rdd1.reduce(_+_)// collect 將RDD轉換為數組 rdd1.collect// top 返回最大的k個元素 rdd1.top(2)

    9. 如何保證kafka的消息不丟失

    https://blog.csdn.net/liudashuang2017/article/details/88576274

    我們可以從三個方面保證kafka不丟失消息

    • 首先從producer生產者方面,為send()方法注冊一個回調函數,可以得知消息發送有沒有成功;將重試次數retrie設置為3;設置acks參數為all,當消息被寫入所有同步副本之后才算發送成功。
    • 在consumer消費者方面,關閉自動提交;
    • 在broker集群方面,設置復制系數replica.factor為大于等于3

    10. kafka如何選舉leader

    首先啟動的broker在zookeeper中創建一個臨時節點并讓自己稱為leader,其他的節點會創建watch對象進行監聽并成為follower,當broker宕機的時候,其他follower會嘗試創建這個臨時節點,但是只有一個能夠創建成功,創建成功的broker就會成為leader。

    11. 說下spark中的寬依賴和窄依賴

    https://blog.csdn.net/a1043498776/article/details/54889922

    • 寬依賴:指子RDD的分區依賴于父RDD的所有分區,舉例:groupbykey,join
    • 窄依賴:指父RDD的每個分區被最多一個子RDD的分區所依賴,舉例:map,filter

    12. 說下spark中stage是依照什么劃分的

    https://zhuanlan.zhihu.com/p/57124273

    spark中的stage其實是一組并行的任務,spark會將多個RDD根據依賴關系劃分成有向無環圖DAG,DAG會被劃分成多個stage,劃分的依據是RDD之間的寬窄依賴。遇到寬依賴就劃分stage。因為寬依賴與窄依賴的區別之一就是寬依賴會發生shuffle操作,所以也可以說stage的劃分依據是是否發生shuffle操作。

    13. spark的內存管理是怎樣的

    https://www.jianshu.com/p/4f1e551553ae

    https://www.cnblogs.com/wzj4858/p/8204282.html

    spark的內存包括靜態內存管理和統一內存管理兩種機制。靜態內存管理中存儲和執行兩塊內存區域是分開的,統一內存管理中兩塊內存之間可以相互借用

    • 靜態內存管理:靜態內存管理機制下堆內內存包括安全內存,存儲內存,shuffle內存和unroll內存

    • 統一內存管理:統一內存管理機制下內存分為spark內存,用戶內存和保留內存三部分。用戶內存存放用戶代碼邏輯和自定義數據結構等,保留內存存放的是spark的內部對象和邏輯。

    14. spark的容錯機制是什么樣的

    https://blog.csdn.net/dengxing1234/article/details/73613484

    spark的容錯機制是通過血統(lineage)和checkpoint來實現的 。

    • RDD的lineage可以看作是一個重做日志(redo log)記錄的是它粗粒度上的transformation操作。當rdd的分區數據丟失時,它可以根據lineage重新計算來恢復數據。 在窄依賴上可以直接計算父RDD的節點數據進行恢復,在寬依賴上則要等到父RDD所有數據計算完后并將結果shuffle到子RDD上才能完成恢復。
    • 如果DAG中的lineage過長,或者在寬依賴上進行checkpoint的收益更大,就會使用checkpoint進行容錯,將RDD寫入磁盤進行持久化存儲,如果節點數據丟失,就從磁盤讀取數據進行恢復。

    9. HR面

    1. 自我介紹

    (HR面試的自我介紹可以側重軟實力部分,項目技術方面介紹可以適當少一些)

    2. 項目中遇到的最大難點

    • 在項目中曾經遇到了新的框架不知道該如何上手的問題,以及面對新的概念,新的技術不知道從何學起。解決的辦法是在官網尋找說明文檔和demo,按照說明文檔上的內容一步步了解,以及咨詢身邊有用過這個框架的同學,或者在CSDN上尋找相關博客。

    • 項目的時間比較緊迫,沒有那么多的時間可以用。解決方法是把還沒有完成的項目分一個輕重緩急,在有限的時間里,先做重要而且緊急的,然后完成緊急的,再做重要的。利用輕重緩急做一個取舍。

    3. 項目中的收獲

    一個是了解了相關框架的使用方法(比如Dataframe的使用,xgboost的使用等等),這些框架或者技術可以在以后的開發中使用到。和對自己開發能力的鍛煉。

    一個是鍛煉了與他人的交流能力,因為在團隊項目里經常會跟別人匯報自己的想法和進度,同時也會跟其他成員溝通模塊之間的交互,所以在這個過程中對自己的表達能力和理解能力都是一個很大的提升。

    4. 可以實習的時間,實習時長

    一定要往長了說!半年起步,最好七八個月,因為實習生是可以隨時跑路的。而且實習時間越長HR越青睞。

    5. 哪里人

    hr問這個是因為人一般都不會愿意離開家鄉去太遠的地方工作,比如你是北方人,可能就不太可能到深圳來發展。所以如果自己的籍貫家鄉跟base地太遠,記得解釋一下你為什么選擇來這么遠的地方發展,表明你有意愿到這個地方長久發展。

    6. 說一下自己的性格

    我是比較內向謹慎的人,平時做的多說的少。比較善于總結,在與人交流的時候更傾向于傾聽別人的意見后才發言。并且別人都說我辦事認真靠譜。

    7. 你的優缺點是什么

    我的缺點是容易在一些細節的地方花費太多的時間,有時候過分追求細節。并且我的實習經驗比較缺乏,對于實際項目的業務流程和工作流程不是很了解。(所以我打算通過實習來熟悉實際的軟件開發的流程和技術。)

    我的優點是責任心比較強,做事比較負責,在校期間我負責的大創項目進展很順利,我經常組織組員們進行討論和推進項目的開發,最后這個項目得到了92的評分,在同級別里面是比較高的。

    8. 有什么興趣愛好,畫的怎么樣/球打的如何/游戲打的怎么樣

    平時的愛好是畫畫打游戲,在CSDN寫寫博客,還有就是看書,我很喜歡學到新知識掌握新技能的感覺。

    9. 看過最好的一本書是什么

    技術類:編程之美 機器學習西瓜書 STL源碼剖析 劍指offer C++primer plus

    非技術類:明朝那些事兒 香水(聚斯金德) 解憂雜貨店 人類簡史 沉默的大多數 與時間做朋友(李笑來) 千年歷史千年詩

    10. 學習技術中有什么難點

    11. 怎么看待加班

    我覺得 任何一家單位都有可能要加班。如果自己的工作沒有按時完成,那自覺加班是理所當然的,當然,自己要不斷提高工作效率,避免這種原因導致的加班。如果遇到緊急任務或者突發狀況時,為了順利配合團隊完成任務,我會盡自己所能加班共同完成。

    12. 覺得深圳怎么樣(或者其他地點)

    13. 遇見過最大的挫折是什么,怎么解決的

    14. 職業規劃

    在工作的第一個階段,先盡快適應工作的環境,包括開發環境開發工具和工作流程等,把自己負責的部分快速的完成,不能出差錯。第二個階段要熟悉整個項目的業務流程,所有模塊的結構和依賴關系,知道每個模塊為什么要這么設計,以及它們的實現細節。第三個階段要培養獨立設計一個項目的能力,可以獨立或者在別人的協作下設計項目的模塊分工和架構。

    在工作和項目中多寫博客或者筆記,積累技術影響力,將經驗總結成文檔。同時與同事搞好關系,嘗試培養領導能力和組織能力。

    15. 目前的offer情況

    可以如實說

    16. 你最大的優勢和劣勢是什么

    • 優勢:做事情有主動性,不拖沓,有責任心。舉個例子:在做論文課題的時候,幾乎都是我自己找老師匯報進度和找老師討論問題,很少有被老師催的時候。每一次跟老師討論之后都會將討論的內容和老師提出的意見進行詳細記錄。在中軟杯的比賽中,主動承擔答辯ppt的制作,并且每次排練之后都迅速對ppt的修改意見進行落實修改,前前后后改了十幾版。
    • 劣勢:有時候做事情比較急躁,容易導致粗心。

    17. 介紹在項目里面充當的角色

    18. 介紹一下本科獲得的全國賽獎項的情況

    19. 最有成就感的事情/最驕傲的一件事情

    • 本科的時候跟優秀的隊友們一起參加中國軟件杯比賽努力了四個月,最后獲得了該賽題的第一名和全國一等獎的好成績
    • 保研夏令營拿到了四個學校的offer

    20. 在實驗室中擔任什么角色,參加的XXX能聊聊嗎

    22. 用兩個詞來形容自己

    踏實 認真

    總結

    以上是生活随笔為你收集整理的超全面的后端开发C/C++面经整理分享含详细参考答案 包括简历分享的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。