[c/c++後臺開發面經] 京東面經(含答案)

此係列內容以及觀點僅個人感受,不妥之處直接私我!目的爲了大家能更好的知道面試題難度以及如何準備,希望能讓大家少浪費時間尋找資料,多點時間學點乾貨!因爲篇幅原因,大部分題目題解簡潔,但有相關資料推薦進階閱讀,學會使用搜索引擎是一門藝術!望諒解!

當時投遞的時候,崗位只是說了是c/c++開發工程師,到了二面問了面試官才知道我面的是無人駕駛部門。

一 面試情況

此戰終結於技術面最後一面,值得深思。由於內推簡歷沒有直接通過篩選,參加了筆試才並有幸參加了面試,但是一面電話面結束後,面試官說如果有二面希望能現場面!

1 一面(電話面25分鐘)

  • 簡述一下項目

一面提項目,一般說明項目背景,自己做了什麼就好了,不會深問,但是能準備着更好

  • 項目中遇到過什麼問題,怎麼解決

這個問題,凡是涉及項目基本上都跑不了,前面說過需要準備幾個面試官百分之80會問的關於項目的題。

  • 都學過什麼課程,計算機方向是軟件工程嗎

計算機網絡,數據結構,操縱系統,編譯原理,人工智能,大數據等隨便你選幾個,保證自己能說出個123

  • C++中的類的大小計算

C++中類的成員函數,靜態成員是不佔類的大小的。類的大小等於基類的大小+子類個non-static成員變量的大小再+非虛基類大小,如果有多態性還要考慮vptr(可能不止一個)大小,這裏成員變量是會被字節對齊的。

  • 介紹一下http與https及區別

HTTPS和HTTP的區別

超文本傳輸協議HTTP協議被用於在Web瀏覽器和網站服務器之間傳遞信息。HTTP協議以明文方式發送內容,不提供任何方式的數據加密,如果攻擊者截取了Web瀏覽器和網站服務器之間的傳輸報文,就可以直接讀懂其中的信息,因此HTTP協議不適合傳輸一些敏感信息,比如信用卡號、密碼等。(此處可以使用wireshark工具模擬模擬,另外如果學習協議也可以考慮python的scapy/dpkt庫)

爲了解決HTTP協議的這一缺陷,需要使用另一種協議:安全套接字層超文本傳輸協議HTTPS。爲了數據傳輸的安全,HTTPS在HTTP的基礎上加入了SSL協議,SSL依靠證書來驗證服務器的身份,併爲瀏覽器和服務器之間的通信加密。

HTTPS和HTTP的區別主要爲以下四點:

一、https協議需要到ca申請證書,一般免費證書很少,需要交費。

二、http是超文本傳輸協議,信息是明文傳輸,https則是具有安全性的ssl加密傳輸協議(當然不是絕對安全,也可以通過特徵抽取,過濾,篩選,模型訓練進行搞事情!)。

三、http和https使用的是完全不同的連接方式,用的端口也不一樣,前者是80,後者是443。

四、http的連接很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全。

  • 打印int時不小心用了%s會出現什麼問題

  • 段錯誤

 int i = 10;    
 char *s = "12"; 
 printf("%d\n", s); // 數據不對
 printf("%s\n", i); // 段錯誤
  • 鏈表成環(快慢指針,提供思路即可,《劍指offer》)

[c++版本]

[python]版本

[java版本]
  • 邏輯題

1000瓶無色無味的藥水,其中有一瓶毒藥,10只小白鼠拿過來做實驗。喝了無毒的藥水第二天沒事兒,喝了有毒的藥水後第二天會死亡。如何在一天之內(第二天)找出這瓶有毒的藥水?

思路就是用二進制,2^10=1024,也就是10只小白鼠最多能驗出1024瓶藥水,哪個有毒。小白鼠編號,1-10。瓶子也編號,1-1000,然後把瓶子的編號轉變爲二進制數。如果第幾位是1,就把這瓶水給第幾個小白鼠喝。最後大概每個小白鼠喝500瓶藥水的混合液。如果還不懂,下面列幾個數字解釋一下。

瓶子編號 二進制數 第幾個小白鼠喝

1 0000000001 1

2 0000000010 2

3 0000000011 1,2

4 0000000100 3

5 0000000101 1,3

大概就是這意思,再反過來,假如1號和3號小白鼠死了,死的小白鼠用1表示,再寫成2進制數:0000000101,轉化爲十進制數是5,從上面列出來的也可以看出1,3都喝了5號瓶的水,所以就是第五瓶水有毒。

解決方案
1)我們將1000瓶液體編號1~1000,然後將編號轉化爲10位二進制,如1號就是0000000001;


2)將十隻小白鼠編號1~10;


3)將液體的二進制編號上爲1的位數給對應的小白鼠喝,如液體編號爲 1111100000,那就是1~5號小白鼠不喝這瓶液體,6~10號小白鼠喝這瓶液體;


4)一星期後觀察小白鼠的死亡情況,如果1~5號小白鼠死亡,6~10號小白鼠存活,那麼有毒的那瓶液體對應的二進制編碼爲 0000011111;


5)將第四步得到的二進制編碼轉化爲十進制,這裏是31號,因此我們可以推斷出編號爲31的液體是被污染的。

  • cookie 和session 的區別:

cookie和session的共同之處在於:cookie和session都是用來跟蹤瀏覽器用戶身份的會話方式。

cookie 和session 的區別:

1、cookie數據存放在客戶的瀏覽器上,session數據放在服務器上。

2、cookie不是很安全,別人可以分析存放在本地的COOKIE並進行COOKIE欺騙,考慮到安全應當使用session。

3、session會在一定時間內保存在服務器上,超過時間會銷燬這個SESSION。當訪問增多,會比較佔用你服務器的性能考慮到減輕服務器性能方面,應當使用COOKIE。

4、單個cookie保存的數據不能超過4K,很多瀏覽器都限制一個站點最多保存20個cookie。

5、所以個人建議:將登陸信息等重要信息存放爲SESSION,其他信息如果需要保留,可以放在COOKIE中

  • 還有什麼可以問我的嗎

這個問題一般來說會有二面,只要不問一些敏感話題就行了。

第二天早上收到二面通知,但是說需要現場面。思考了半天,決定還是去現場面試,雖然需要差不多單程900的車費,萬一現場面試更簡單呢是吧。當然我也知道,我不去一定會後悔,索性還是去嘗試未嘗不是一件好事,畢竟正好還有小米和滴滴,bigo的現場面試。從南方到北方,郵箱通知上面是九點到西土城的泰富酒店簽到,下火車時間差不多爲六點多,所以到達那裏的時候差不多七點,我是第一個到那裏並簽到的,然後就在那裏看自己準備的算法題,其實昨天晚上在火車上也複習了很久,因爲我會盡全力的去完成這次任務,以至今也沒後悔之言。北京的天空依然那麼的純藍!

[北京西站]

2  二面(現場面)

我們簽到以後,面試官可以看見簽到時間,是一哥很溫柔的小哥,讓我把行李放了坐下,別緊張,先自我介紹,然後他說,你這麼遠過來其實沒必要的,可以申請遠程的,不然太折騰了,今天我們就簡單問問。

  • 自我介紹

自我介紹完了以後

面試官:你覺得你的一面感覺如何

我:我說一面面試官很好(其實我從之前的溝通中已經感覺一面二面是同一個面試官了),不太會的都會引導我,然後回頭查了相關的資料。面試官還是比較滿意的。注意:覆盤很重要,一般都有面試記錄的。

  • 我看你寫了三個項目,說一個熟悉一些的,背景,你做了啥,有什麼難點

面試官:我們看幾個簡單題

  • 構造函數爲什麼不能是虛函數

虛函數的調用需要虛函數表指針,而該指針存放在對象的內容空間中;若構造函數聲明爲虛函數,那麼由於對象還未創建,還沒有內存空間,更沒有虛函數表地址用來調用虛函數。

  • Makefile、GDB應該都用過吧

可以參考陳皓很多年前寫的一個專欄,如果沒找到電子版可以私我!

  • 原子變量和volatile區別(C++11)

Volatile變量可以確保先行關係,即寫操作會發生在後續的讀操作之前, 但它並不能保證原子性。例如用volatile修飾count變量那麼 count++ 操作就不是原子性的。而AtomicInteger類提供的atomic方法可以讓這種操作具有原子性如getAndIncrement()方法會原子性的進行增量操作把當前值加一,其它數據類型和引用變量也可以進行相似操作。

  • 智能指針介紹(C++11)

1.auto_ptr主要是用來解決資源自動釋放的問題;auto_ptr支持賦值和複製,將指針的所有權轉移,但是如果轉移後再訪問原來得指針,行爲不確定,程序可能會在運行時出錯。

2.unique_ptr與auto_ptr一樣,也是建立所有權機制,但是不支持複製和賦值,所以將一個unique_ptr對象賦值給另一個時,程序編譯出錯;但如果將臨時的unique_ptr賦值或複製給另一個對象時,沒有問題。unique_ptr比auto_ptr更安全。

3.shared_ptr和unique_ptr都只能一個智能指針引用對象,而shared_ptr則是可以多個智能指針同時擁有一個對象。shared_ptr實現方式就是使用引用計數引用計數的原理是,多個智能指針同時引用一個對象,每當引用一次,引用計數加一,每當智能指針銷燬了,引用計數就減一,當引用計數減少到0的時候就釋放引用的對象。這種引用計數的增減發生在智能指針的構造函數,複製構造函數,賦值操作符,析構函數中。

這種方式使得多個智能指針同時對所引用的對象有擁有權,同時在引用計數減到0之後也會自動釋放內存,也實現了auto_ptr和unique_ptr的資源釋放的功能。

4.weak_ptr,shared_ptr是一種強引用的關係,智能指針直接引用對象。那麼這個會代碼一個隱含的問題,就是循環引用,從而造成內存泄漏,首先來看一個循環引用的例子。

class Parent

{

public:

  shared_ptr<Child> child;

};

class Child

{

public:

  shared_ptr<Parent> parent;

};

void Function()

{

shared_ptr<Parent> pA(new Parent);

shared_ptr<Child> pB(new Child);

pA->child = pB;

pB->parent = pA;

}

//第一條語句使得pA引用了Parent一個指針,Parent引用計數爲1

//第二條語句使得pB引用了Child一個指針,Child引用計數爲1

//第三條語句,調用了shared_ptr<Child>類的賦值操作符,使得Child引用計數變爲2

//第四條語句,調用了shared_ptr<Parent>類的賦值操作符,使得Parent引用計數變爲

//函數返回之前調用了shared_ptr<Parent>和shared_ptr<Child>類的析夠函數,使得Child引用計數變爲1,Parent引用計數變爲1

咱們看,函數執行完之後new出來的Parent和Child並沒有釋放,所以出現了內存泄漏(說出這幾個字之前,自己應該至少知道哪些工具可以檢測內存泄漏等相關問題)

出現泄漏的原因就是pA和pB相互引用了,導致兩者所引用對象的引用計數不能減少到0,造成泄漏。

如果把第三條語句或者第四條語句任意刪除一個,就不會有泄漏了。這就是強引用所帶來的問題。weak_ptr從字面意思上可以看出是一個弱指針,不是說明這個指針的能力比較弱,而是說他對他所引用的對象的所有權比較弱,說得更直接一點兒就是他並不擁有所引用對象的所有權,而且他還不能直接使用他所引用的對象。

在stl中,weak_ptr是和shared_ptr配合使用的,在實現shared_ptr的時候也就考慮了weak_ptr的因素。weak_ptr是shared_ptr的觀察者,它不會干擾shared_ptr所共享對象的所有權,當一個weak_ptr所觀察的shared_ptr要釋放它的資源時,它會把相關的weak_ptr的指針設置爲空,防止weak_ptr持有懸空的指針。注意:weak_ptr並不擁有資源的所有權,所以不能直接使用資源。可以從一個weak_ptr構造一個shared_ptr以取得共享資源的所有權。

weak_ptr是爲配合shared_ptr而引入的一種智能指針,它更像是shared_ptr的一個助手,而不是智能指針,因爲它不具有普通指針的行爲,沒有重載operator*和operator->,它的最大作用在於協助shared_ptr,像旁觀者那樣觀測資源的使用情況。

weak_ptr被設計爲與shared_ptr共同工作,可以從一個shared_ptr或者另一個weak_ptr對象構造,獲得資源的觀測權。但weak_ptr沒有共享資源,它的構造不會引起指針引用計數的增加。同樣,在weak_ptr析構時也不會導致引用計數的減少,它只是一個靜靜地觀察者。

使用weak_ptr的成員函數use_count()可以觀測資源的引用計數,另一個成員函數expired()的功能等價於use_count() == 0,但更快,表示觀測的資源(也就是shared_ptr管理的資源)已經不復存在了。

weak_ptr 沒有重載operator*和->,這是特意的,因爲它不共享指針,不能操作資源,這是它弱的原因。但它可以使用一個非常重要的成員函數lock()從被觀測的shared_ptr獲得一個可用的shared_ptr對象,從而操作資源。當expired() == true的時候,lock()函數將返回一個存儲空指針的shared_ptr。

  • 智能指針內部實現(C++11)

智能指針類將一個計數器與類指向的對象相關聯,引用計數跟蹤該類有多少個對象共享同一指針。每次創建類的新對象時,初始化指針並將引用計數置爲1;


當對象作爲另一對象的副本而創建時,拷貝構造函數拷貝指針並增加與之相應的引用計數;對一個對象進行賦值時,賦值操作符減少左操作數所指對象的引用計數(如果引用計數爲減至0,則刪除對象),並增加右操作數所指對象的引用計數;

調用析構函數時,構造函數減少引用計數(如果引用計數減至0,則刪除基礎對象)。智能指針就是模擬指針動作的類。所有的智能指針都會重載 -> 和 * 操作符。智能指針還有許多其他功能,比較有用的是自動銷燬。這主要是利用棧對象的有限作用域以及臨時對象(有限作用域實現)析構函數釋放內存。

  • DPDK內部實現(這個是因爲簡歷上有寫,關於一個高性能數據包處理庫)

Winpcap:它的一個流程是npf網絡組包過濾器首先負責從網絡中採集數據包,完成數據的過濾拷貝到內核緩存區,然後調用相應的動態庫文件將數據傳遞到應用層緩衝區,最後應用程序處理。具體工作原理

(1)網卡接受數據包到達信息,然後產生硬件中斷,通知cpu調度處理,中斷服務程序判斷數據包的有效性,分配一個緩衝。

(2)BPF模塊根據用戶的規則過濾數據包,並把數據包插入到內核的網卡驅動緩衝隊列中。

(3)用戶程序通過系統調用用來讀取內核緩衝區的數據包 完成數據採集。

優化方案

(1)使用雙緩衝減少線程鎖。

(2)多線程。

(3)將原始的數據包還原成流保存 減少對數據包的存儲,在內核層提供了通用socket環形緩衝,不進入內核協議棧,最後在應用層通過socket鏈接同時使用mmap技術直接訪問socket環狀緩衝區

  • libevent結構、內部實現(回調+同步)、多線程實現

首先I/O複用經過封裝;

統一事件源(I/O事件,信號事件,定時事件);

事件處理提前註冊(回調函數)。

Libevent是線程不安全的,但是libevent提供了鎖機制,而且在實現框架上儘量避免使用鎖,像memcache多線程使用libevent,他的實現現架是主線程監聽到讀寫事件分發任務(使用CQ隊列),每個工作線程對應一個CQ隊列。

上面提到的兩個網絡庫可以去看看,資料很全的。

  • epoll內部實現

紅黑樹 就緒事件雙向鏈表;每當就緒事件到來時,通過實現註冊好的回調函數將就緒事件加入到就緒事件隊列中,epoll_wait返回時只需要遍歷就緒事件雙向鏈表。

  • timewait作用

客戶端收到服務的釋放連接的請求後,不是立馬進入CLOSE狀態,而是還要再等待2MSL。理由是:

  • 確保最後一個確認報文能夠到達。如果沒能到達,服務端就會會重發FIN請求釋放連接。等待一段時間沒有收到重發就說明服務的已經CLOSE了。如果有重發,則客戶端再發送一次LAST ack信號

  • 等待一段時間是爲了讓本連接持續時間內所產生的所有報文都從網絡中消失,使得下一個新的連接不會出現舊的連接請求報文

  • 編程題1 手撕快排

  • 編程題2 二分查找變種

二分查找以及變種的總結

3 總結

這次面試難度還行,甚至沒有涉及到分佈式,微服務,負載均衡等問題。最終雖然敗北,但是學會了一下幾點:

  • 公司招你去是幹活了,不會因爲你怎麼怎麼的而降低對你的要求標準。

  • 在工具上寫代碼和手撕代碼完全不一樣。

  • 珍惜每一次面試機會並學會覆盤

  • 對於應屆生主要考察的還是計算機基礎知識的掌握,項目要求沒有那麼高,是自己做的項目就使勁摳細節,做測試。只有這樣,才知道會遇到什麼問題,遇到什麼難點,如何解決的。從而可以侃侃而談了。

點【在看】是最大的支持 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章