字節(jié)客戶端也瘋狂拷打基礎!
大家好,我是小林。
關注我的同學,有很多都是學C++的同學,針對互聯(lián)網(wǎng)后端崗位的話,C++可能沒有太多優(yōu)勢,因為很少項目是用 C++ 做后端業(yè)務類型的開發(fā)了,主流的還是 java 和 go 后端。
但是,很多互聯(lián)網(wǎng)客戶端崗位會喜歡 C++ 同學,因為學 C++的同學,通常計算機基礎都還不錯,很多人可能不太知道客戶端崗位具體會問什么,其實主要也是圍繞 C++、網(wǎng)絡、操作系統(tǒng)、算法,這四大塊問了,像數(shù)據(jù)庫、消息隊列后端組件這些就不會問了。
這次就分享一位 C++同學,面字節(jié)客戶端崗位的面經(jīng),都是圍繞C++、網(wǎng)絡、操作系統(tǒng)、算法這四大塊內(nèi)容去問了。
C++
C++中的內(nèi)存分區(qū)有哪些?
在C++中,內(nèi)存主要分為以下五個區(qū)域:
圖片
- 棧區(qū)(Stack):由編譯器自動分配釋放,存放函數(shù)的參數(shù)值,局部變量等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
- 堆區(qū)(Heap):一般由程序員分配釋放,若程序員不釋放,程序結(jié)束時可能由OS回收。注意,與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式倒是類似于鏈表。
- 全局區(qū)(靜態(tài)區(qū))(Static):全局變量和靜態(tài)變量被分配到同一塊內(nèi)存中。在C++中,全局區(qū)還包含了常量區(qū),字符串常量和其他常量也是存儲在此。
- 常量區(qū):是全局區(qū)的一部分,存放常量,不允許修改。
- 代碼區(qū)(Text):存放函數(shù)體的二進制代碼。
介紹一下內(nèi)存對齊
內(nèi)存對齊就是就是將數(shù)據(jù)存放在內(nèi)存的某個位置,使得CPU可以更快地訪問到這個數(shù)據(jù),以空間換時間的方式來提高 cpu 訪問數(shù)據(jù)的性能。
在C++中,內(nèi)存對齊主要涉及到兩個概念:對齊邊界和填充字節(jié)。
- 對齊邊界:一般情況下,編譯器會自動地將數(shù)據(jù)存放在它的自然邊界上。例如,int類型的數(shù)據(jù),它的大小為4字節(jié),編譯器會將其存放在4的倍數(shù)的地址上。這就是所謂的對齊邊界。
- 填充字節(jié):為了滿足對齊邊界的要求,編譯器有時候需要在數(shù)據(jù)之間填充一些字節(jié)。這些字節(jié)沒有實際的意義,只是為了滿足內(nèi)存對齊的要求。
為什么要字節(jié)對齊?
- 平臺原因(移植原因):不是所有的硬件平臺都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。
- 性能原因:數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應該盡可能地在自然邊界上對齊。原因在于,為了訪問未對齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問;而對齊的內(nèi)存訪問僅需要一次訪問。
vector中push_back和emplace_back的區(qū)別?
- push_back() 向容器尾部添加元素時,首先會創(chuàng)建這個元素,然后再將這個元素拷貝或者移動到容器中(如果是拷貝的話,事后會自行銷毀先前創(chuàng)建的這個元素);
- 而emplace_back() 在實現(xiàn)時,則是直接在容器尾部創(chuàng)建這個元素,省去了拷貝或移動元素的過程。
C++中的多態(tài)怎么實現(xiàn)的?
C++中的多態(tài)主要通過虛函數(shù)和繼承來實現(xiàn)。多態(tài)分為兩種:編譯時多態(tài)和運行時多態(tài)。
- 編譯時多態(tài):也稱為靜態(tài)多態(tài)或早綁定。這種多態(tài)是通過函數(shù)重載和模板來實現(xiàn)的。
- 運行時多態(tài):也稱為動態(tài)多態(tài)或晚綁定。這種多態(tài)是通過虛函數(shù)和繼承來實現(xiàn)的。當基類的指針或引用指向派生類對象時,調(diào)用的虛函數(shù)將是派生類的版本,這就實現(xiàn)了運行時多態(tài)。
什么是純虛函數(shù)?有哪些應用場景
純虛函數(shù)是在基類中聲明的虛函數(shù),它在基類中沒有定義,但要求任何派生類都要定義自己的實現(xiàn)方法。在C++中,純虛函數(shù)的聲明形式如下:
virtual void function() = 0;
其中,= 0就表示這是一個純虛函數(shù)。
含有純虛函數(shù)的類被稱為抽象類。抽象類不能被實例化,只能作為接口使用。派生類必須實現(xiàn)所有的純虛函數(shù),否則該派生類也會變成抽象類。
純虛函數(shù)的應用場景主要包括:
- 設計模式:例如在模板方法模式中,基類定義一個算法的骨架,而將一些步驟延遲到子類中。這些需要在子類中實現(xiàn)的步驟就可以聲明為純虛函數(shù)。
- 接口定義:可以創(chuàng)建一個只包含純虛函數(shù)的抽象類作為接口。所有實現(xiàn)該接口的類都必須提供這些函數(shù)的實現(xiàn)。
為什么一般將析構(gòu)函數(shù)設置為虛函數(shù)?
析構(gòu)函數(shù)被設為虛函數(shù)主要是為了解決基類指針指向派生類對象時的資源釋放問題。
如果我們有一個基類指針,它實際上指向一個派生類對象,當我們刪除這個基類指針時,如果析構(gòu)函數(shù)不是虛函數(shù),那么就只會調(diào)用基類的析構(gòu)函數(shù),而不會調(diào)用派生類的析構(gòu)函數(shù)。這可能會導致派生類對象的一些資源沒有被正確釋放,從而引發(fā)內(nèi)存泄漏等問題。
如果我們將析構(gòu)函數(shù)設置為虛函數(shù),那么在刪除基類指針時,會首先調(diào)用派生類的析構(gòu)函數(shù),然后再調(diào)用基類的析構(gòu)函數(shù),從而確保所有的資源都能被正確釋放。
什么是內(nèi)聯(lián)函數(shù)?
在C++中,使用關鍵字"inline"可以聲明一個內(nèi)聯(lián)函數(shù)。聲明為內(nèi)聯(lián)函數(shù)的函數(shù)會在編譯時被視為候選項,編譯器會嘗試將其展開,將函數(shù)體直接插入到調(diào)用點處。這樣可以避免函數(shù)調(diào)用的開銷,減少了函數(shù)調(diào)用的棧幀等額外開銷,從而提高程序的執(zhí)行效率。
內(nèi)聯(lián)函數(shù)有什么缺點?
內(nèi)聯(lián)函數(shù)的缺點主要有以下幾點:
- 代碼膨脹:內(nèi)聯(lián)函數(shù)會在每個調(diào)用它的地方進行代碼替換,這可能導致代碼膨脹。如果內(nèi)聯(lián)函數(shù)體非常大或者被頻繁調(diào)用,會增加可執(zhí)行文件的大小,可能導致緩存不命中,影響性能。
- 編譯時間增加:內(nèi)聯(lián)函數(shù)需要在每個調(diào)用點進行代碼替換,這會增加編譯時間。特別是當內(nèi)聯(lián)函數(shù)被廣泛使用時,編譯時間可能會顯著增加。
- 可讀性降低:內(nèi)聯(lián)函數(shù)會將函數(shù)體嵌入到調(diào)用點,可能導致代碼的可讀性降低。函數(shù)體被分散在多個地方,可能會使代碼難以理解和維護。
網(wǎng)絡
http狀態(tài)碼有哪些?
五大類 HTTP 狀態(tài)碼
圖片
- 1xx 類狀態(tài)碼屬于提示信息,是協(xié)議處理中的一種中間狀態(tài),實際用到的比較少。
- 2xx 類狀態(tài)碼表示服務器成功處理了客戶端的請求,也是我們最愿意看到的狀態(tài)。
- 3xx 類狀態(tài)碼表示客戶端請求的資源發(fā)生了變動,需要客戶端用新的 URL 重新發(fā)送請求獲取資源,也就是重定向。
- 4xx 類狀態(tài)碼表示客戶端發(fā)送的報文有誤,服務器無法處理,也就是錯誤碼的含義。
- 5xx 類狀態(tài)碼表示客戶端請求報文正確,但是服務器處理時內(nèi)部發(fā)生了錯誤,屬于服務器端的錯誤碼。
http1.1、2.0版本的區(qū)別?
HTTP/1.1和HTTP/2.0是兩個不同版本的HTTP協(xié)議,它們之間有以下幾個主要區(qū)別:
- 多路復用:HTTP/1.1中,每個請求都需要建立一個獨立的連接,而HTTP/2.0引入了多路復用技術,允許在同一個連接上同時發(fā)送多個請求和接收多個響應,提高了并發(fā)性能。
- 二進制分幀:HTTP/2.0使用二進制分幀機制,將請求和響應數(shù)據(jù)分割為更小的幀,每個幀都有自己的標識和優(yōu)先級,可以獨立傳輸和處理,提高了數(shù)據(jù)傳輸?shù)撵`活性和效率。
- 首部壓縮:HTTP/2.0使用了首部壓縮算法,減少了請求和響應的首部大小,節(jié)省了帶寬和傳輸時間。
- 服務器推送:HTTP/2.0支持服務器推送,服務器可以在客戶端請求之前主動將相關資源推送給客戶端,減少了額外的請求延遲。
在瀏覽器輸入URL之后,具體流程是什么?
具體的流程如下:
- URL解析:瀏覽器首先解析輸入的URL,提取出協(xié)議、主機名、端口號、路徑等信息。
- DNS解析:瀏覽器將主機名轉(zhuǎn)換為對應的IP地址,通過DNS解析來完成這一步驟。
- 建立TCP連接:瀏覽器與服務器之間建立TCP連接,通過三次握手建立可靠的連接。
- 發(fā)送HTTP請求:瀏覽器構(gòu)建HTTP請求報文,包括請求方法(GET、POST等)、請求頭部、請求體等信息,并將其發(fā)送給服務器。
- 服務器處理請求:服務器接收到請求后,根據(jù)請求的路徑、參數(shù)等進行處理,并生成對應的HTTP響應。
- 接收HTTP響應:瀏覽器接收到服務器返回的HTTP響應報文,包括響應狀態(tài)碼、響應頭部、響應體等信息。
- 渲染頁面:瀏覽器根據(jù)接收到的響應數(shù)據(jù),解析HTML、CSS、JavaScript等資源,并進行頁面的渲染,展示給用戶。
- 關閉TCP連接:頁面渲染完成后,瀏覽器與服務器之間的TCP連接會被關閉,釋放網(wǎng)絡資源。
tcp 是怎么實現(xiàn)可靠傳輸?shù)模?/h3>- 序列號與確認應答:TCP將每個發(fā)送的數(shù)據(jù)包進行編號(序列號),接收方通過發(fā)送確認應答(ACK)來告知發(fā)送方已成功接收到數(shù)據(jù)。如果發(fā)送方在一定時間內(nèi)未收到確認應答,會進行超時重傳。
- 數(shù)據(jù)校驗:TCP使用校驗和來驗證數(shù)據(jù)的完整性。接收方會計算接收到的數(shù)據(jù)的校驗和,并與發(fā)送方發(fā)送的校驗和進行比較,以檢測數(shù)據(jù)是否在傳輸過程中發(fā)生了錯誤。
- 窗口控制:TCP使用滑動窗口機制來控制發(fā)送方和接收方之間的數(shù)據(jù)流量。發(fā)送方根據(jù)接收方的處理能力和網(wǎng)絡狀況來調(diào)整發(fā)送的數(shù)據(jù)量,接收方則通過窗口大小來告知發(fā)送方可以接收的數(shù)據(jù)量。
- 重傳機制:如果發(fā)送方未收到確認應答或接收方檢測到數(shù)據(jù)錯誤,TCP會進行重傳。發(fā)送方會根據(jù)超時時間或接收方的冗余確認來觸發(fā)重傳,以確保數(shù)據(jù)的可靠傳輸。
- 擁塞控制:TCP使用擁塞控制算法來避免網(wǎng)絡擁塞。通過動態(tài)調(diào)整發(fā)送速率和窗口大小,TCP可以根據(jù)網(wǎng)絡的擁塞程度來進行適當?shù)恼{(diào)整,以提高網(wǎng)絡的利用率和穩(wěn)定性。
IP數(shù)據(jù)報的報頭有哪些字段?
圖片
IP數(shù)據(jù)報的報頭包含以下字段:
- 版本(Version):指定IP協(xié)議的版本,通常為IPv4或IPv6。
- 首部長度(Header Length):指定IP報頭的長度,以32位字(4字節(jié))為單位。
- 服務類型(Type of Service):用于指定數(shù)據(jù)報的服務質(zhì)量要求,如優(yōu)先級、延遲、吞吐量等。
- 總長度(Total Length):指定整個IP數(shù)據(jù)報的長度,包括報頭和數(shù)據(jù)部分。
- 標識(Identification):用于唯一標識一個IP數(shù)據(jù)報,通常由發(fā)送方設置,接收方用于重組分片。
- 標志(Flags):包含3個標志位,分別是DF(Don't Fragment,不分片)、MF(More Fragments,更多分片)、和保留位。
- 分片偏移(Fragment Offset):用于指示當前分片相對于原始數(shù)據(jù)報的偏移量,以8字節(jié)為單位。
- 生存時間(Time to Live):指定數(shù)據(jù)報在網(wǎng)絡中可以經(jīng)過的最大路由器跳數(shù),每經(jīng)過一個路由器,該值減1,為0時數(shù)據(jù)報被丟棄。
- 協(xié)議(Protocol):指定IP數(shù)據(jù)報中承載的上層協(xié)議,如TCP、UDP、ICMP等。
- 頭部校驗和(Header Checksum):用于檢驗IP報頭的完整性,接收方使用該字段來驗證報頭是否正確。
- 源IP地址(Source IP Address):指定發(fā)送方的IP地址。
- 目標IP地址(Destination IP Address):指定接收方的IP地址。
IP 報文的TTL是什么意思?
指定數(shù)據(jù)報在網(wǎng)絡中可以經(jīng)過的最大路由器跳數(shù)。每當數(shù)據(jù)報經(jīng)過一個路由器時,該字段的值會減少1。當TTL的值為0時,路由器將丟棄該數(shù)據(jù)報并發(fā)送ICMP的時間超過消息給源主機。
TTL的主要目的是防止數(shù)據(jù)報在網(wǎng)絡中無限循環(huán),避免由于路由環(huán)路或其他問題導致的數(shù)據(jù)報無法正常到達目的地。通過限制數(shù)據(jù)報的最大跳數(shù),TTL可以確保數(shù)據(jù)報在有限的時間內(nèi)能夠到達目標主機或被丟棄,以避免網(wǎng)絡資源的浪費和延遲。
操作系統(tǒng)
虛擬地址是怎么轉(zhuǎn)化到物理地址的?
圖片
虛擬地址到物理地址的轉(zhuǎn)換是通過操作系統(tǒng)中的內(nèi)存管理單元(MMU,Memory Management Unit)來完成的。下面是一般的虛擬地址到物理地址轉(zhuǎn)換過程:
- 程序發(fā)出內(nèi)存訪問請求時,使用虛擬地址進行訪問。
- 虛擬地址被傳遞給MMU進行處理。
- MMU中的地址映射表(頁表)被用來將虛擬地址轉(zhuǎn)換為物理地址。頁表是一種數(shù)據(jù)結(jié)構(gòu),用于存儲虛擬地址和物理地址之間的映射關系。
- MMU根據(jù)頁表中的映射關系,將虛擬地址轉(zhuǎn)換為對應的物理地址。
- 轉(zhuǎn)換后的物理地址被傳遞給內(nèi)存系統(tǒng),用于實際的內(nèi)存訪問操作。
頁表是怎么構(gòu)成的?
頁表是一種數(shù)據(jù)結(jié)構(gòu),用于存儲虛擬地址和物理地址之間的映射關系。多級頁表將頁表分為多個層級,每個層級的頁表項存儲下一級頁表的物理地址。通過多級索引,可以逐級查找,最終找到對應的物理頁。
對于 64 位的系統(tǒng),主要有四級目錄,分別是:
- 全局頁目錄項 PGD
- 上層頁目錄項 PUD
- 中間頁目錄項 PMD
- 頁表項 PTE
圖片
進程間通信有哪些?
- 管道(Pipe):管道是一種半雙工的通信方式,可以在父子進程或者具有親緣關系的進程之間進行通信。管道可以是匿名管道(使用pipe函數(shù)創(chuàng)建)或有名管道(使用mkfifo函數(shù)創(chuàng)建)。
- 信號(Signal):信號是一種異步的通信方式,用于通知進程發(fā)生了某個事件。進程可以通過系統(tǒng)調(diào)用signal或sigaction來注冊信號處理函數(shù),當接收到特定信號時,會調(diào)用相應的處理函數(shù)進行處理。
- 共享內(nèi)存(Shared Memory):共享內(nèi)存是一種高效的通信方式,允許多個進程共享同一塊物理內(nèi)存區(qū)域。進程可以通過映射共享內(nèi)存到自己的地址空間,實現(xiàn)對共享數(shù)據(jù)的讀寫。
- 信號量(Semaphore):信號量是一種用于進程同步和互斥的機制。進程可以使用信號量來控制對共享資源的訪問,實現(xiàn)進程之間的同步和互斥。
- 消息隊列(Message Queue):消息隊列是一種有序的消息傳遞機制,進程可以通過消息隊列發(fā)送和接收消息。消息隊列提供了一種可靠的通信方式,可以實現(xiàn)進程之間的異步通信。
- 套接字(Socket):套接字是一種網(wǎng)絡編程接口,也可以用于進程間通信。進程可以通過套接字進行網(wǎng)絡通信,也可以通過本地套接字(Unix Domain Socket)實現(xiàn)本地進程間通信。
共享內(nèi)存是怎么實現(xiàn)的?
共享內(nèi)存的機制,就是拿出一塊虛擬地址空間來,映射到相同的物理內(nèi)存中。這樣這個進程寫入的東西,另外一個進程馬上就能看到了,都不需要拷貝來拷貝去,傳來傳去,大大提高了進程間通信的速度。
圖片
操作系統(tǒng)原子操作怎么實現(xiàn)的?
操作系統(tǒng)中的原子性操作是通過硬件和軟件的支持來實現(xiàn)的。在多核處理器上,原子性操作需要保證在多個核心之間的并發(fā)執(zhí)行中的正確性和一致性。
硬件層面上,現(xiàn)代處理器提供了一些特殊的指令或機制來支持原子性操作,例如原子交換(atomic exchange)、原子比較并交換(atomic compare-and-swap)等。這些指令能夠在執(zhí)行期間禁止中斷或其他核心的干擾,確保操作的原子性。
軟件層面上,操作系統(tǒng)提供了一些原子性操作的接口或函數(shù),例如原子操作函數(shù)(atomic operation),它們使用了硬件提供的原子性指令來實現(xiàn)原子性操作。這些函數(shù)通常是在內(nèi)核態(tài)下執(zhí)行,可以保證在多個進程或線程之間的原子性。
操作系統(tǒng)還可以使用鎖機制來實現(xiàn)原子性操作。例如,互斥鎖(mutex)可以用來保護共享資源的訪問,只有持有鎖的進程或線程可以訪問共享資源,其他進程或線程需要等待鎖的釋放。通過鎖的機制,可以保證對共享資源的原子性操作。
算法
- 算法:島嶼數(shù)量
- 算法:股票
其他
- 你對自己的職業(yè)規(guī)劃是什么?
- 平時是怎么學習的?
- 做項目過程中遇到的最大困難是什么?
- 哪個項目你覺得收益最大?