拼多多、腾讯 C++开发工程师面试题
?
(一)拼多多實(shí)習(xí)服務(wù)端
1、 一個(gè)C++源文件從文本到可執(zhí)行文件經(jīng)歷的過(guò)程
對(duì)于C/C++編寫(xiě)的程序,從源代碼到可執(zhí)行文件,一般經(jīng)過(guò)下面四個(gè)步驟:
1).預(yù)處理,產(chǎn)生.ii文件
2).編譯,產(chǎn)生匯編文件(.s文件)
3).匯編,產(chǎn)生目標(biāo)文件(.o或.obj文件)
4).鏈接,產(chǎn)生可執(zhí)行文件(.out或.exe文件)
2、#include 的順序以及尖叫括號(hào)和雙引號(hào)的區(qū)別
1. #include的順序的區(qū)別:
頭文件的引用順序?qū)τ诔绦虻木幾g還是有一定影響的。如果要在文件a.h中聲明一個(gè)在文件b.h中定義的變量,而不引用b.h。那么要在a.c文件中引用b.h文件,并且要先引用b.h,后引用a.h,否則匯報(bào)變量類型未聲明錯(cuò)誤,也就是常見(jiàn)的某行少個(gè)“;”符號(hào)。
2. #include尖括號(hào)和雙引號(hào)的區(qū)別:
1)#include <> ,認(rèn)為該頭文件是標(biāo)準(zhǔn)頭文件。編譯器將會(huì)在預(yù)定義的位置集查找該頭文件,這些預(yù)定義的位置可以通過(guò)設(shè)置查找路徑環(huán)境變量或者通過(guò)命令行選項(xiàng)來(lái)修改。使用的查找方式因編譯器的不同而差別迥異。
2)#include "",認(rèn)為它是非系統(tǒng)頭文件,非系統(tǒng)頭文件的查找通常開(kāi)始于源文件所在的路徑。查找范圍大于<>。
3、進(jìn)程和線程,為什么要有線程
1、和進(jìn)程相比,它是一種非常"節(jié)儉"的多任務(wù)操作方式。在linux系統(tǒng)下,啟動(dòng)一個(gè)新的進(jìn)程必須分配給它獨(dú)立的地址空間,建立眾多的數(shù)據(jù)表來(lái)維護(hù)它的代碼段、堆棧段和數(shù)據(jù)段,這是一種"昂貴"的多任務(wù)工作方式。(資源)
2、運(yùn)行于一個(gè)進(jìn)程中的多個(gè)線程,它們之間使用相同的地址空間,而且線程間彼此切換所需時(shí)間也遠(yuǎn)遠(yuǎn)小于進(jìn)程間切換所需要的時(shí)間。據(jù)統(tǒng)計(jì),一個(gè)進(jìn)程的開(kāi)銷大約是一個(gè)線程開(kāi)銷的30倍左右。(切換效率)
3、線程間方便的通信機(jī)制。對(duì)不同進(jìn)程來(lái)說(shuō),它們具有獨(dú)立的數(shù)據(jù)空間,要進(jìn)行數(shù)據(jù)的傳遞只能通過(guò)進(jìn)程間通信的方式進(jìn)行,這種方式不僅費(fèi)時(shí),而且很不方便。線程則不然,由于同一進(jìn)城下的線程之間貢獻(xiàn)數(shù)據(jù)空間,所以一個(gè)線程的數(shù)據(jù)可以直接為其他線程所用,這不僅快捷,而且方便。(通信)
除以上優(yōu)點(diǎn)外,多線程程序作為一種多任務(wù)、并發(fā)的工作方式,還有如下優(yōu)點(diǎn):
1、使多CPU系統(tǒng)更加有效。操作系統(tǒng)會(huì)保證當(dāng)線程數(shù)不大于CPU數(shù)目時(shí),不同的線程運(yùn)行于不同的CPU上。(CPU設(shè)計(jì)保證)
2、改善程序結(jié)構(gòu)。一個(gè)既長(zhǎng)又復(fù)雜的進(jìn)程可以考慮分為多個(gè)線程,成為幾個(gè)獨(dú)立或半獨(dú)立的運(yùn)行部分,這樣的程序才會(huì)利于理解和修改。(代碼易維護(hù))
4、C++11有哪些新特性
1)關(guān)鍵字及新語(yǔ)法:auto、nullptr、for
2)STL容器:std::array、std::forward_list、std::unordered_map、std::unordered_set
3)多線程:std::thread、std::atomic、std::condition_variable
4)智能指針內(nèi)存管理:std::shared_ptr、std::weak_ptr
5)其他:std::function、std::bind和lamda表達(dá)式
5、為什么可變參數(shù)模板至關(guān)重要,右值引用,完美轉(zhuǎn)發(fā),lambda
6、malloc的原理,brk系統(tǒng)調(diào)用干什么的,mmap呢
malloc的實(shí)現(xiàn)方案:
1)malloc 函數(shù)的實(shí)質(zhì)是它有一個(gè)將可用的內(nèi)存塊連接為一個(gè)長(zhǎng)長(zhǎng)的列表的所謂空閑鏈表。
2)調(diào)用 malloc()函數(shù)時(shí),它沿著連接表尋找一個(gè)大到足以滿足用戶請(qǐng)求所需要的內(nèi)存塊。 然后,將該內(nèi)存塊一分為二(一塊的大小與用戶申請(qǐng)的大小相等,另一塊的大小就是剩下來(lái)的字節(jié))。 接下來(lái),將分配給用戶的那塊內(nèi)存存儲(chǔ)區(qū)域傳給用戶,并將剩下的那塊(如果有的話)返回到連接表上。
3)調(diào)用 free 函數(shù)時(shí),它將用戶釋放的內(nèi)存塊連接到空閑鏈表上。
4)到最后,空閑鏈會(huì)被切成很多的小內(nèi)存片段,如果這時(shí)用戶申請(qǐng)一個(gè)大的內(nèi)存片段, 那么空閑鏈表上可能沒(méi)有可以滿足用戶要求的片段了。于是,malloc()函數(shù)請(qǐng)求延時(shí),并開(kāi)始在空閑鏈表上檢查各內(nèi)存片段,對(duì)它們進(jìn)行內(nèi)存整理,將相鄰的小空閑塊合并成較大的內(nèi)存塊。
brk和mmap:
從操作系統(tǒng)角度來(lái)看,進(jìn)程分配內(nèi)存有兩種方式,分別由兩個(gè)系統(tǒng)調(diào)用完成:brk和mmap(不考慮共享內(nèi)存)。
1、brk是將數(shù)據(jù)段(.data)的最高地址指針_edata往高地址推;
2、mmap是在進(jìn)程的虛擬地址空間中(堆和棧中間,稱為文件映射區(qū)域的地方)找一塊空閑的虛擬內(nèi)存。
這兩種方式分配的都是虛擬內(nèi)存,沒(méi)有分配物理內(nèi)存。在第一次訪問(wèn)已分配的虛擬地址空間的時(shí)候,發(fā)生缺頁(yè)中斷,操作系統(tǒng)負(fù)責(zé)分配物理內(nèi)存,然后建立虛擬內(nèi)存和物理內(nèi)存之間的映射關(guān)系。
在標(biāo)準(zhǔn)C庫(kù)中,提供了malloc/free函數(shù)分配釋放內(nèi)存,這兩個(gè)函數(shù)底層是由brk,mmap,munmap這些系統(tǒng)調(diào)用實(shí)現(xiàn)的。
7、C++的內(nèi)存管理方式,STL的allocator,最新版本默認(rèn)使用的分配器
C++的內(nèi)存管理方式:
在c++中內(nèi)存主要分為5個(gè)存儲(chǔ)區(qū):
棧(Stack):局部變量,函數(shù)參數(shù)等存儲(chǔ)在該區(qū),由編譯器自動(dòng)分配和釋放.棧屬于計(jì)算機(jī)系統(tǒng)的數(shù)據(jù)結(jié)構(gòu),進(jìn)棧出棧有相應(yīng)的計(jì)算機(jī)指令支持,而且分配專門的寄存器存儲(chǔ)棧的地址,效率分高,內(nèi)存空間是連續(xù)的,但棧的內(nèi)存空間有限。
堆(Heap):需要程序員手動(dòng)分配和釋放(new,delete),屬于動(dòng)態(tài)分配方式。內(nèi)存空間幾乎沒(méi)有限制,內(nèi)存空間不連續(xù),因此會(huì)產(chǎn)生內(nèi)存碎片。操作系統(tǒng)有一個(gè)記錄空間內(nèi)存的鏈表,當(dāng)收到內(nèi)存申請(qǐng)時(shí)遍歷鏈表,找到第一個(gè)空間大于申請(qǐng)空間的堆節(jié)點(diǎn),將該節(jié)點(diǎn)分配給程序,并將該節(jié)點(diǎn)從鏈表中刪除。一般,系統(tǒng)會(huì)在該內(nèi)存空間的首地址處記錄本次分配的內(nèi)存大小,用于delete釋放該內(nèi)存空間。
全局/靜態(tài)存儲(chǔ)區(qū):全局變量,靜態(tài)變量分配到該區(qū),到程序結(jié)束時(shí)自動(dòng)釋放,包括DATA段(全局初始化區(qū))與BSS段(全局未初始化段)。其中,初始化的全局變量和靜態(tài)變量存放在DATA段,未初始化的全局變量和靜態(tài)變量存放在BSS段。BSS段特點(diǎn):在程序執(zhí)行前BSS段自動(dòng)清零,所以未初始化的全局變量和靜態(tài)變量在程序執(zhí)行前已經(jīng)成為0.
文字常量區(qū):存放常量,而且不允許修改。程序結(jié)束后由系統(tǒng)釋放。
程序代碼區(qū):存放程序的二進(jìn)制代碼
?
SGI 版本STL的默認(rèn)配置器std::alloc
參見(jiàn):《STL源碼剖析》
1)考慮到小型區(qū)塊所可能造成的內(nèi)存碎片問(wèn)題,SGI設(shè)計(jì)了雙層配置器。第一級(jí)配置器直接使用malloc()和free();第二級(jí)則視情況采取不同的策略:當(dāng)配置區(qū)塊超過(guò)128bytes時(shí),視為“足夠大”,便調(diào)用第一級(jí)配置器;當(dāng)配置區(qū)塊小于128bytes時(shí),視之為“過(guò)小”,為了降低額外負(fù)擔(dān),便采用memory pool(內(nèi)存池)整理方式,而不在求助于第一級(jí)配置器。
2)內(nèi)存池的核心:內(nèi)存池和16個(gè)自由鏈表(各自管理8,16,...,128bytes的小額區(qū)塊)。在分配一個(gè)小區(qū)塊時(shí),首先在所屬自由鏈表中尋找,如果找到,直接抽出分配;若所屬自由鏈表為空,則請(qǐng)求內(nèi)存池為所屬自由鏈表分配空間;默認(rèn)情況下,為該自由鏈表分配20個(gè)區(qū)塊,若內(nèi)存池剩余容量不足,則分配可分配的最大容量;若內(nèi)存池連一個(gè)區(qū)塊都無(wú)法分配,則調(diào)用chunk_alloc為內(nèi)存池分配一大塊區(qū)塊;若內(nèi)存不足,則嘗試調(diào)用malloc分配,否則返回bad_alloc異常。
8、hash表的實(shí)現(xiàn),包括STL中的哈希桶長(zhǎng)度常數(shù)。
hash表的實(shí)現(xiàn)主要涉及兩個(gè)問(wèn)題:散列函數(shù)和碰撞處理。
1)hash function (散列函數(shù))。最常見(jiàn)的散列函數(shù):f(x) = x % TableSize .
2)碰撞問(wèn)題(不同元素的散列值相同)。解決碰撞問(wèn)題的方法有許多種,包括線性探測(cè)、二次探測(cè)、開(kāi)鏈等做法。SGL版本使用開(kāi)鏈法,使用一個(gè)鏈表保持相同散列值的元素。
雖然開(kāi)鏈法并不要求表格大小必須為質(zhì)數(shù),但SGI STL仍然以質(zhì)數(shù)來(lái)設(shè)計(jì)表格大小,并且將28個(gè)質(zhì)數(shù)(逐漸呈現(xiàn)大約兩倍的關(guān)系)計(jì)算好,以備隨時(shí)訪問(wèn),同時(shí)提供一個(gè)函數(shù),用來(lái)查詢?cè)谶@28個(gè)質(zhì)數(shù)之中,“最接近某數(shù)并大于某數(shù)”的質(zhì)數(shù)。
9、hash表如何rehash,怎么處理其中保存的資源
先想想為什么需要rehash:
因?yàn)?#xff0c;當(dāng)loadFactor(負(fù)載因子)<=1時(shí),hash表查找的期望復(fù)雜度為O(1). 因此,每次往hash表中添加元素時(shí),我們必須保證是在loadFactor <1的情況下,才能夠添加。
模仿C++的vector擴(kuò)容方式,Hash表中每次發(fā)現(xiàn)loadFactor==1時(shí),就開(kāi)辟一個(gè)原來(lái)桶數(shù)組的兩倍空間(稱為新桶數(shù)組),然后把原來(lái)的桶數(shù)組中元素全部轉(zhuǎn)移過(guò)來(lái)到新的桶數(shù)組中。注意這里轉(zhuǎn)移是需要元素一個(gè)個(gè)重新哈希到新桶中的。
?
?
?
?
?
?
(二)騰訊二面面經(jīng)
1、redis的主從復(fù)制怎么做的
Redis舊版復(fù)制功能只有同步和命令傳播。新版復(fù)制功能加入了部分同步的功能。
1)同步:
2)命令傳播:
當(dāng)主服務(wù)器會(huì)將自己執(zhí)行的寫(xiě)命令,也即是造成主從服務(wù)器不一致的那條寫(xiě)命令,發(fā)送給從服務(wù)器執(zhí)行,當(dāng)從服務(wù)器執(zhí)行了相同的寫(xiě)命令之后,主從服務(wù)器將再次回到一致?tīng)顟B(tài)。
3)部分同步:(斷線后重復(fù)制)
復(fù)制偏移量:通過(guò)對(duì)比主從服務(wù)器的復(fù)制偏移量,程序可以很容易地知道主從服務(wù)器是否處于一致?tīng)顟B(tài)。
復(fù)制積壓緩沖區(qū):主服務(wù)保存最近的寫(xiě)命令到復(fù)制積壓緩沖區(qū),是一個(gè)先進(jìn)先出隊(duì)列
服務(wù)器運(yùn)行ID:從服務(wù)器記錄上次同步的主服務(wù)器的Id。
2、寫(xiě)代碼,去掉字符串中的空格空格
#include <iostream>using namespace std;int main(){char str[40] = " abc 123 456 ";int num = 0;int i;for(i = 0; str[i] != '\0'; ++i){if(str[i] == ' ')++num;elsestr[i-num] = str[i];}str[i-num] = '\0';printf("%s\n",str); }3、如何把一個(gè)文件快速下發(fā)到100w個(gè)服務(wù)器
gossip算法?Gossip有眾多的別名“閑話算法”、“疫情傳播算法”、“病毒感染算法”、“謠言傳播算法”。
4、如何判斷一個(gè)圖是否連同?
DFS、BFS、并查集
如果大家對(duì)C/C++感興趣的話,可以加一下我們的學(xué)習(xí)交流Q群:637 ?935 ?295,免費(fèi)領(lǐng)取一套學(xué)習(xí)資料和視頻課程喲~
5、ubuntu開(kāi)機(jī)的時(shí)候系統(tǒng)做了什么
1)加載BIOS
BIOS程序首先檢查,計(jì)算機(jī)硬件能否滿足運(yùn)行的基本條件,這叫做”硬件自檢”。硬件自檢完成后,BIOS把控制權(quán)轉(zhuǎn)交給下一階段的啟動(dòng)程序。
2)讀取MBR
計(jì)算機(jī)讀取該設(shè)備的第一個(gè)扇區(qū),也就是讀取最前面的512個(gè)字節(jié)。如果這512個(gè)字節(jié)的最后兩個(gè)字節(jié)是0x55和0xAA,表明這個(gè)設(shè)備可以用于啟動(dòng);如果不是,表明設(shè)備不能用于啟動(dòng),控制權(quán)于是被轉(zhuǎn)交給”啟動(dòng)順序”中的下一個(gè)設(shè)備。
3)Bootloader
在這種情況下,計(jì)算機(jī)讀取”主引導(dǎo)記錄”前面446字節(jié)的機(jī)器碼之后,不再把控制權(quán)轉(zhuǎn)交給某一個(gè)分區(qū),而是運(yùn)行事先安裝的”啟動(dòng)管理器”(boot loader),由用戶選擇啟動(dòng)哪一個(gè)操作系統(tǒng)。
Boot Loader 就是在操作系統(tǒng)內(nèi)核運(yùn)行之前運(yùn)行的一段小程序。通過(guò)這段小程序,我們可以初始化硬件設(shè)備、建立內(nèi)存空間的映射圖,從而將系統(tǒng)的軟硬件環(huán)境帶到一個(gè)合適的狀態(tài),以便為最終調(diào)用操作系統(tǒng)內(nèi)核做好一切準(zhǔn)備。
Boot Loader有若干種,其中Grub、Lilo和spfdisk是常見(jiàn)的Loader。Linux環(huán)境中,目前最流行的啟動(dòng)管理器是Grub。
4)加載內(nèi)核
內(nèi)核的加載,內(nèi)核加載后,接開(kāi)始操作系統(tǒng)初始化,根據(jù)進(jìn)程的優(yōu)先級(jí)啟動(dòng)進(jìn)程。
總結(jié)
以上是生活随笔為你收集整理的拼多多、腾讯 C++开发工程师面试题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 个人珍藏的小众软件
- 下一篇: 云炬VB开发笔记 1初始Visual