国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

C++八股文

這篇具有很好參考價(jià)值的文章主要介紹了C++八股文。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

C++語言

int function(int a[], int b)

? C++中,定義函數(shù) int function(int a[], int b),這里數(shù)組a會(huì)不會(huì)在內(nèi)存中拷貝(傳遞的是指針還是啥),什么情況下傳遞的是指針?

? 不會(huì),因?yàn)檫@里的 a 傳遞的是指針,和 int * 是一樣的。

指針數(shù)組和數(shù)組指針的區(qū)別?

優(yōu)先級(jí):() > [] > *

c++八股文,c++,數(shù)據(jù)結(jié)構(gòu),算法

數(shù)組指針

? 是一個(gè)指針,指向一個(gè)數(shù)組的起始地址。由于 [] 運(yùn)算符的優(yōu)先級(jí)比 * 運(yùn)算符高,所以定義時(shí)需要使用小括號(hào)將 * 運(yùn)算符與指針名括起來,表示這里定義的變量是個(gè)指針,括號(hào)外寫數(shù)組中變量的類型,以及數(shù)組的大小。

指針數(shù)組

? 是一個(gè)數(shù)組,數(shù)組的每個(gè)元素都是指針。定義時(shí)不需要小括號(hào),變量名先與 [] 運(yùn)算符結(jié)合,表示這是一個(gè)數(shù)組,前面的 * 運(yùn)算符表示數(shù)組中的內(nèi)容是指針。

函數(shù)指針和指針函數(shù)的區(qū)別

函數(shù)指針

? 是一個(gè)指針,指針指向的地址是一個(gè)函數(shù)的入口地址。通過該指針可以調(diào)用目標(biāo)函數(shù)。定義時(shí)需要用小括號(hào)將 * 運(yùn)算符與指針名括起來。

指針函數(shù)

? 是一個(gè)函數(shù),函數(shù)的返回值是一個(gè)指針類型的變量。

常量指針和指針常量的區(qū)別

  • **常量指針:**指針本身是個(gè)常量,只想不能修改
  • **指針常量:**指向的是一個(gè)常量

數(shù)組和指針的區(qū)別

  • **賦值:**同類型的指針變量可以相互賦值,數(shù)組就不能相互賦值,只能一個(gè)元素一個(gè)元素的拷貝
  • **存儲(chǔ)方式:**數(shù)組在內(nèi)存中連續(xù)存放,必須開辟一段連續(xù)的內(nèi)存空間,而指針和普通變量的存儲(chǔ)方式相同
  • 求 sizeof: 64位操作系統(tǒng)中指針大小固定 8 字節(jié),而數(shù)組則是一整片連續(xù)的內(nèi)存大小

指針和引用的區(qū)別

  • 指針是一個(gè)變量,存儲(chǔ)的是一個(gè)地址,而引用是原變量的別名,和原變量是同一個(gè)東西
  • 指針可以有多級(jí),也就是存在指向指針的指針,而引用只能是一級(jí)
  • 指針在定義的時(shí)可以不初始化,引用必須在定義的時(shí)初始化
  • 引用一旦定義之后,就與變量綁定,無法再引用別的變量,而指針可以隨時(shí)改變指向
  • 64位操作系統(tǒng)中,指針在內(nèi)存中的大小恒為8字節(jié),而引用的大小和引用的變量大小一致
  • 自增運(yùn)算符的意義不同,指針自增就是指向當(dāng)前元素的下一個(gè)元素,而引用則是根據(jù)引用元素的類型做加法
  • 引用可以直接使用,而指針獲取指向的值要用 * 運(yùn)算符
  • 當(dāng)把指針作為參數(shù)進(jìn)行傳遞時(shí),也是將實(shí)參的一個(gè)拷貝傳遞給形參,兩者指向的地址相同,但不是同一個(gè)變量,在函數(shù)中改變這個(gè)變量的指向不影響實(shí)參,而引用卻可以。

數(shù)組名 和 指針的區(qū)別?

  • 二者都可通過增減偏移量來訪問數(shù)組中的元素。
  • 數(shù)組名不是真正意義上的指針,可以理解為常指針,所以數(shù)組名沒有自增、自減等操作。
  • 當(dāng)數(shù)組名當(dāng)做形參傳遞給調(diào)用函數(shù)后,就失去了原有特性,退化成一般指針,多了自增、自減操作,但sizeof運(yùn)算符不能再得到原數(shù)組的大小了。

在函數(shù)傳參時(shí),什么時(shí)候用指針,什么時(shí)候用引用?

  • 參數(shù)需要作為傳出參數(shù)時(shí),使用指針
  • 對(duì)??臻g大小比較敏感時(shí)用引用,引用傳參不會(huì)創(chuàng)建臨時(shí)對(duì)象,可以避免棧溢出
  • 類對(duì)象作為參數(shù)傳遞時(shí)要用引用,這是 C++ 類對(duì)象傳遞的標(biāo)準(zhǔn)方式

原始字面量

? 通過R"()"的方式可以定義一個(gè)原始字面量,它可以直接得到原始意義的字符串,而不需要額外對(duì)字符串做轉(zhuǎn)義或連接等操作。

? 通過使用原始字面量可以簡化一些打印操作。例如輸出某個(gè)文件的路徑,可以不用再寫轉(zhuǎn)義字符,輸出多行字符串時(shí)不再需要使用連接符。

auto

? C++ 提供了 auto 和 decltype 來靜態(tài)推導(dǎo)類型,在我們知道類型沒有問題但?不想完整地寫出類型的時(shí)候, 就可以使?靜態(tài)類型推導(dǎo)。 decltype ?于獲取?個(gè)表達(dá)式的類型,?不對(duì)表達(dá)式進(jìn)?求值。

注意:

? auto定義的變量必須有初始值。

? auto的自動(dòng)類型推斷發(fā)生在編譯期,所以使用auto并不會(huì)造成程序運(yùn)行時(shí)效率的降低。

? 編譯器可以根據(jù)初始值自動(dòng)推導(dǎo)出類型。但是不能用于函數(shù)傳參以及數(shù)組類型的推導(dǎo)。

? auto可以推斷基本類型,也可以推斷引用類型,當(dāng)推斷引用類型時(shí)候,將引用對(duì)象的類型作為推斷類型。

decltype

? 當(dāng)需要某個(gè)表達(dá)式的返回值類型而又不想實(shí)際執(zhí)行它時(shí)用decltype。decltype是為了解決復(fù)雜的類型聲明而使用的關(guān)鍵字。

與auto的區(qū)別:

? auto忽略頂層const,decltype保留頂層const

? 對(duì)引用操作,auto推斷出原有類型,decltype推斷出引用

? 對(duì)解引用操作,auto推斷出原有類型,decltype推斷出引用

? auto推斷時(shí)會(huì)實(shí)際執(zhí)行表達(dá)式,decltype不會(huì)執(zhí)行表達(dá)式,只做分析

有了auto為什么還需要decltype?

decltype 可以獲得編譯期的類型。 auto不能。所以當(dāng)你需要某個(gè)表達(dá)式的返回值類型而又不想實(shí)際執(zhí)行它時(shí)用decltype。

頂層 const 和 底層 const

  • 頂層const原理,對(duì)象本身是 const 的。是說頂層const對(duì)于拷貝無關(guān)緊要,其實(shí)是因?yàn)榭截惖氖侵?,沒拷那些修飾符;
  • 底層const原理,指向的,或者綁定的,是 const 的。是說底層const必須保持一致,其實(shí)是原對(duì)象的可寫性(可修改性)在賦值過程中,不應(yīng)有所擴(kuò)大。假設(shè)原來不能修改,不能傳著傳著就可以修改了。假設(shè)原來能修改,那傳著傳著不能修改了,問題也不大?!痪湓挘褐羔樤儋x值過程中原對(duì)象的可寫性不能擴(kuò)大,但是可以縮小。

const 的作用

  • 定義只讀常量,先不分配內(nèi)存,放入符號(hào)表,如果程序中對(duì) 常量取了地址,系統(tǒng)才會(huì)給它開辟空間。如果是用一個(gè)變量初始化常量時(shí),也會(huì)開辟空間,不會(huì)放入符號(hào)表。const 修飾自定義類型時(shí),也會(huì)開辟空間。image-20230310095818603
  • 使用 const 和 & 共同修飾函數(shù)參數(shù)類型,可以避免函數(shù)傳參時(shí)的拷貝開銷,提高程序效率
  • const 可以修飾成員方法,表示該方法絕對(duì)不會(huì)修改成員變量,如果不小心修改了,編譯器會(huì)報(bào)錯(cuò)
  • const 修飾函數(shù)返回值類型,使得函數(shù)調(diào)用表達(dá)式不能作為左值。修飾函數(shù)返回的指針或引用類型,使得返回值不為左值,從而保護(hù)指針指向的內(nèi)容或引用的內(nèi)容不被修改,常用于運(yùn)算符重載(返回成員屬性的運(yùn)算符重載都會(huì)用到)。

說?下 C++ ?是怎么定義常量的?常量存放在內(nèi)存的哪個(gè)位置?

對(duì)于局部常量,存放在棧區(qū);
對(duì)于全局常量,存放在靜態(tài)存儲(chǔ)區(qū);
字?值常量,存放在常量存儲(chǔ)區(qū)。

C++ const 和 C 語言區(qū)別?

  • C 語言中的 const 功能單一,只能用來定義常量,雖然也能修飾函數(shù)的參數(shù)和返回值,但功能性都不是很強(qiáng);而 C++ 中的 const 和 & 共同修飾函數(shù)參數(shù)可以避免參數(shù)進(jìn)行拷貝,提高程序效率,還可以修飾成員方法,表明該方法不會(huì)修改成員屬性。修飾函數(shù)返回值類型,使得函數(shù)調(diào)用表達(dá)式不能作為左值。
  • C 語言中的 const 不是絕對(duì)安全的,C 語言中的局部 const 存放在棧區(qū),可以通過指針間接修改 const 的值。而 C++ 的 const 可以保證絕對(duì)安全。

智能指針

? 智能指針用來動(dòng)態(tài)的分配內(nèi)存,當(dāng)構(gòu)造時(shí)分配內(nèi)存,當(dāng)離開作用域時(shí),自動(dòng)釋放已分配的內(nèi)存。使用智能指針能幫助程序員更簡單的管理動(dòng)態(tài)內(nèi)存,解決很多潛在的內(nèi)存泄漏的問題。

? 智能指針本身是一個(gè)棧上分配的對(duì)象。根據(jù)棧上分配的特性,在離開作用域后,編譯器會(huì)調(diào)用其析構(gòu)函數(shù),從而達(dá)到自動(dòng)釋放內(nèi)存的效果。

? unique指針無法共享所有權(quán),只能有一個(gè)指針可以指向被管理的對(duì)象。unique_ptr 的拷貝構(gòu)造函數(shù)和賦值運(yùn)算符都是 delete 的,所以 unique_ptr 不能復(fù)制。但它有接受右值引用的拷貝構(gòu)造和賦值運(yùn)算符,所以可以通過轉(zhuǎn)移語義將所有權(quán)轉(zhuǎn)移到另外一個(gè)unique_ptr。(unique_ptr 比 auto_ptr 更安全)

? shared指針可以共享所有權(quán),可以存在多個(gè)指針指向同一個(gè)對(duì)象,當(dāng)最后一個(gè)shared指針離開作用域時(shí)才會(huì)釋放內(nèi)存。shared指針內(nèi)部有一個(gè)共享計(jì)數(shù)器來自動(dòng)管理,計(jì)數(shù)器實(shí)際上就是指向該資源指針的個(gè)數(shù),每當(dāng)有一個(gè)shared指針指向該資源,引用計(jì)數(shù)就 + 1。當(dāng)一個(gè)shared指針離開作用域時(shí),引用計(jì)數(shù) - 1,當(dāng)引用計(jì)數(shù)為 0 時(shí),就會(huì)釋放內(nèi)存。

可以通過成員函數(shù) use_count() 來查看資源的所有者個(gè)數(shù),除了可以通過 new 來構(gòu)造,還可以通過傳?unique_ptr,weak_ptr 來構(gòu)造。當(dāng)我們調(diào)? release() 時(shí),當(dāng)前指針會(huì)釋放資源所有權(quán),計(jì)數(shù)減?。當(dāng)計(jì)數(shù)等于 0 時(shí),資源會(huì)被釋放。

? weak指針是一個(gè)不控制對(duì)象生命周期的弱指針,它可以指向shared指針管理的內(nèi)存,但不會(huì)改變引用計(jì)數(shù),也不能直接調(diào)用原生指針的方法。weak指針主要是為了解決 shared_ptr 循環(huán)引用造成的內(nèi)存泄漏問題。由于shared指針通過引用計(jì)數(shù)來管理原生指針,那么循環(huán)引用就會(huì)導(dǎo)致內(nèi)存泄漏,而weak指針不會(huì)增加引用計(jì)數(shù),將循環(huán)引用改為弱引用就可以避免內(nèi)存泄漏。

和 shared_ptr 之間可以相互轉(zhuǎn)化, shared_ptr 可以直接賦值給它,它可以通過調(diào)? lock 函數(shù)來獲得shared_ptr

? 分配內(nèi)存空間時(shí),用shared指針;引用對(duì)象的地方,使用weak指針。

? 引用計(jì)數(shù)加減是原子操作,是線程安全的,而shared指針讀寫并不是線程安全的。

問題:

  1. 用weak指針對(duì)象如何判斷該指針指向的對(duì)象是否銷毀?

    答:weak_ptr 類中有一個(gè)成員函數(shù) lock() ,這個(gè)函數(shù)可以返回指向共享對(duì)象的 shared_ptr,如果 weak 指針?biāo)赶虻馁Y源不存在,那么 lock 函數(shù)返回一個(gè)空 shared 指針,通過這個(gè)可以判斷。要通過 weak 指針訪問資源也要用類似的方法,因?yàn)?weak_ptr 沒有重載 operator* 和 operator->,所以要先獲取到對(duì)應(yīng)的 shared 指針,判斷是否為空,然后再訪問資源、

lock_guard 和 unique_lock

? C++11中新增了 lock_guard 可以防止線程使用 mutex 加鎖后異常退出導(dǎo)致死鎖的問題。lock_guard 創(chuàng)建時(shí)自動(dòng)加鎖,當(dāng)離開作用域時(shí)自動(dòng)解鎖。使用起來非常方便。這一點(diǎn)和智能指針很像,lock_guard 也是利用了棧上分配的對(duì)象離開作用域時(shí)編譯器自動(dòng)調(diào)用其析構(gòu)函數(shù)的特性,實(shí)現(xiàn)了自動(dòng)解鎖。lock_guard 內(nèi)部封裝了一個(gè)普通鎖,他的構(gòu)造函數(shù)中進(jìn)行枷鎖操作,析構(gòu)函數(shù)中進(jìn)行解鎖操作。lock_guard 的拷貝構(gòu)造和等號(hào)賦值運(yùn)算符都是 delete 的,所以只能用在簡單的臨界區(qū)代碼段的互斥操作中。

? unique_lock 和 lock_guard 類似,也能做到創(chuàng)建時(shí)自動(dòng)加鎖,離開作用域自動(dòng)解鎖,不過 unique_lock 可以手動(dòng)釋放鎖,還可以通過創(chuàng)建時(shí)傳入?yún)?shù)設(shè)置是否上鎖。同時(shí)他有接受右值引用的拷貝構(gòu)造函數(shù)和等號(hào)賦值運(yùn)算符,因此可以在函數(shù)調(diào)用中使用。

lambda表達(dá)式

? lambda 表達(dá)式提供了一種類似匿名函數(shù)的特性,而匿名函數(shù)是在需要一個(gè)函數(shù),但又不想費(fèi)力去命名的情況下使用的。通過使用 lambda 表達(dá)式可以編寫內(nèi)嵌的匿名函數(shù),用來替換獨(dú)立函數(shù)或者函數(shù)對(duì)象,使代碼更簡潔可讀,讓開發(fā)更高效。

? lambda 表達(dá)式的原理是,每當(dāng)定義一個(gè) lambda 表達(dá)式,編譯器都會(huì)自動(dòng)生成一個(gè)匿名類,這個(gè)類重載了小括號(hào)運(yùn)算符,實(shí)際調(diào)用的就是重載的小括號(hào)運(yùn)算符。

? lambda 表達(dá)式可以分為五個(gè)部分,捕獲列表、參數(shù)列表、可選項(xiàng)、返回值類型、以及函數(shù)體。其中除了捕獲列表和函數(shù)體,都是可以省略的。捕獲列表即可以按值捕獲,也可以按引用捕獲,按值捕獲的變量實(shí)際上是原變量的拷貝,而且只能讀不能寫,如果要修改,就需要加上 mutable 可選項(xiàng)。

? lambda 表達(dá)式的一個(gè)重要應(yīng)用是可以用于函數(shù)的參數(shù),通過這種方式可以實(shí)現(xiàn)回調(diào)函數(shù)。最常見的就是在 STL 算法中,比如你要統(tǒng)計(jì)一個(gè)數(shù)組中滿足特殊條件的元素?cái)?shù)量,通過 lambda 表達(dá)式給出條件,然后將其傳遞給 count_if 函數(shù)。

int val = 3;
vector<int> v {1, 8, 5, 3, 6, 10};
int count = std::count_if(v.beigin(), v.end(), [val](int x) { return x > val; });
// v中?于3的元素?cái)?shù)量

atomic

C++11對(duì) int、char 這類基本數(shù)據(jù)類型進(jìn)行了原子封裝,使得同一時(shí)刻只能有一個(gè)線程對(duì)其訪問,效率比互斥鎖更高,實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)的無鎖設(shè)計(jì)。

nullptr

? nullptr 出現(xiàn)的目的是為了替代 NULL。NULL 并不是嚴(yán)格意義上的空,而是 0。使用 cout 輸出 NULL,輸出的結(jié)果是0。NULL會(huì)導(dǎo)致 C++ 的重載特性發(fā)生混亂,例如下面這兩個(gè)函數(shù):

void func(int);
void func(int *);

? 如果 NULL 被定義為 0 那么 func(NULL) 這條語句會(huì)去調(diào)用 func(int) ,這就違反了語義。為了解決這個(gè)問題,C++11 引入了 nullptr,專門用來區(qū)分空指針和0,nullptr 就是嚴(yán)格意義上的空指針。使用 cout 輸出 nullptr 的話編譯器會(huì)報(bào)錯(cuò)。

final 和 override

C++ 借助虛函數(shù)實(shí)現(xiàn)了運(yùn)行時(shí)多態(tài),但 C++ 的虛函數(shù)有很多脆弱的地方:

  • 例如無法禁止子類重寫虛函數(shù)。實(shí)際項(xiàng)目中可能繼承到某一層級(jí)時(shí),不希望子類繼續(xù)重寫某個(gè)虛函數(shù)了。
  • 還有就是容易不小心隱藏父類的虛函數(shù),比如在重寫時(shí),不小心聲明了一個(gè)簽名不一致但同名的新函數(shù)。

為了解決這個(gè)問題,C++11 提供了 final 來禁止虛函數(shù)被重寫或禁止類被繼承,override 來顯式的重寫虛函數(shù)。這樣編譯器就能給一些不小心的行為提供錯(cuò)誤和警告。

default 和 delete

主動(dòng)讓編譯器生成默認(rèn)的構(gòu)造函數(shù)。delete則相反。

內(nèi)存對(duì)齊的作用

? 如果內(nèi)存沒有對(duì)?,寄存器存取數(shù)據(jù)要進(jìn)?很多額外操作,??降低了 CPU 的性能。

結(jié)構(gòu)體和聯(lián)合體的區(qū)別

結(jié)構(gòu)體

? 結(jié)構(gòu)體是把不同類型的數(shù)據(jù)組合成一個(gè)整體。struct 里每個(gè)成員都有自己獨(dú)立的地址。sizeof(struct) 是內(nèi)存對(duì)齊后所有成員長度的加和。

聯(lián)合體

? 各成員共享一段內(nèi)存空間, 一個(gè)union變量的長度等于各成員中最長的長度。共同體可被賦予任意成員的值,但每次只能賦一種值, 賦入新值則沖去舊值。 sizeof(union)是最長的數(shù)據(jù)成員的長度。

函數(shù)傳遞參數(shù)的?種?式

  • 值傳遞: 形參是實(shí)參的拷?,函數(shù)內(nèi)部對(duì)形參的操作并不會(huì)影響到外部的實(shí)參。
  • 指針傳遞: 也是值傳遞的?種?式,只不過形參是指向?qū)崊⒌刂返闹羔?,?dāng)對(duì)形參的指向操作時(shí),就相當(dāng)于對(duì)實(shí)參本身進(jìn)?操作。
  • 引?傳遞: 就是把引?對(duì)象的地址放在了開辟的??臻g中,函數(shù)內(nèi)部對(duì)形參的任何操作可以直接映射到外部的實(shí)參上?。

堆 和 棧

? 由編譯器進(jìn)?管理,在需要時(shí)由編譯器?動(dòng)分配空間,在不需要時(shí)候?動(dòng)回收空間,?般保存的是局部變量和函數(shù)參數(shù)等。

? 棧是一段連續(xù)的內(nèi)存空間,在函數(shù)調(diào)?的時(shí)候,?先?棧的是主函數(shù)中下?條可執(zhí)?指令的地址,然后是函數(shù)的各個(gè)參數(shù)。一次函數(shù)調(diào)用結(jié)束時(shí),局部變量先出棧,然后是參數(shù),最后是棧頂指針最開始存放的指令地址,程序由該點(diǎn)繼續(xù)運(yùn)行,不會(huì)產(chǎn)生碎片。

? 棧是高地址向低地址擴(kuò)展,空間較小。

? 堆由程序員管理,需要手動(dòng)分配和回收,如果不進(jìn)行回收會(huì)造成內(nèi)存泄漏的問題。

? 堆是不連續(xù)的空間,系統(tǒng)中有一個(gè)空閑鏈表,當(dāng)有程序申請(qǐng)時(shí),系統(tǒng)遍歷空閑鏈表找到第一個(gè)大于等于申請(qǐng)大小的空間分配給程序,一般在分配的時(shí)候,也會(huì)把空間的起始地址寫入內(nèi)存,方便后續(xù) delete 回收空間。如果有剩余空間也會(huì)插入到空閑鏈表中,因此堆中會(huì)產(chǎn)生碎片。

? 堆是低地址向高地址擴(kuò)展的,空間很大。

堆棧對(duì)比:

  • 管理方式:棧由系統(tǒng)管理,堆由程序員管理
  • 分配效率:棧由系統(tǒng)分配,操作系統(tǒng)會(huì)在底層對(duì)棧提供支持,會(huì)分配專門的寄存器存放棧的地址,速度快;堆由由C++庫函數(shù)提供實(shí)現(xiàn),分配速度慢
  • 申請(qǐng)大小限制不同:棧的大小有限制而且很小,可以進(jìn)行改變;堆空間很大,受限于計(jì)算機(jī)的虛擬地址。
  • 碎片問題:對(duì)于堆頻繁使用new/delete會(huì)產(chǎn)生碎片;棧的數(shù)據(jù)先進(jìn)后出,進(jìn)出一一對(duì)應(yīng),不會(huì)產(chǎn)生碎片
  • 擴(kuò)展方式:棧低地址向高地址擴(kuò)展;堆反著來

C++ 程序的內(nèi)存分區(qū)

  • **棧區(qū):**由編譯器自動(dòng)分配釋放
  • **堆區(qū):**一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時(shí)由 OS 回收。
  • **全局或靜態(tài)存儲(chǔ)區(qū):**全局變量和靜態(tài)變量都存儲(chǔ)在這里。C 語言中,未初始化的放在 .bss 段中,初始化的放在 .data 段中,C++ 中不再區(qū)分。程序結(jié)束后由操作系統(tǒng)釋放。
  • **常量存儲(chǔ)區(qū):**存放字面值常量,程序結(jié)束后由操作系統(tǒng)釋放。
  • **代碼區(qū):**存放編譯后的二進(jìn)制文件,不允許修改。

new/delete 和 malloc/free的區(qū)別

  • 前者是C++運(yùn)算符,后者是C/C++語言標(biāo)準(zhǔn)庫函數(shù)
  • new自動(dòng)計(jì)算要分配的空間大小,malloc需要程序員自己計(jì)算
  • new是類型安全的,malloc不是,可以用 malloc 申請(qǐng) double 類型變量的空間賦值給 int 類型的指針
  • new / delete 會(huì)調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù),而 malloc / free 不會(huì),用他們給對(duì)象分配空間會(huì)很危險(xiǎn)
  • malloc / free 返回的是 void 類型的指針,必須進(jìn)行強(qiáng)轉(zhuǎn),new / delete 返回具體類型的指針

函數(shù)調(diào)用過程

? 在主函數(shù)中遇到一個(gè)函數(shù)調(diào)用時(shí),?先將主函數(shù)中下?條可執(zhí)?指令的地址入棧,以便函數(shù)調(diào)用結(jié)束后可以返回主函數(shù)繼續(xù)運(yùn)行。然后入棧函數(shù)的參數(shù),入棧順序是從右到左。然后然后跳轉(zhuǎn)到該函數(shù)的入口地址開始執(zhí)行,如果函數(shù)中有局部變量則也會(huì)入棧。一次函數(shù)調(diào)用結(jié)束時(shí),局部變量先出棧,然后是參數(shù),最后是棧頂指針最開始存放的指令地址,程序由該點(diǎn)繼續(xù)運(yùn)行。

STL

STL包含6大部件:容器、迭代器、算法、仿函數(shù)、適配器和空間配置器。

  • 容器:容納一組元素的對(duì)象,提供各種數(shù)據(jù)結(jié)構(gòu)。
  • 迭代器:提供一種訪問容器中每個(gè)元素的方法,從實(shí)現(xiàn)的角度來說,迭代器是一種將operator*, operator->, operator++等指針操作賦予 重載的類模板。
  • 仿函數(shù):一個(gè)行為類似函數(shù)的對(duì)象,調(diào)用它就像調(diào)用函數(shù)一樣,重載了operator()的類或者類模板。
  • 算法:包括查找算法、排序算法等等。
  • 適配器:用來修飾容器等,比如 queue 和 stack ,底層借助了 deque。
  • 空間配置器:負(fù)責(zé)空間配置和管理,是一個(gè)實(shí)現(xiàn)了動(dòng)態(tài)空間配置,空間管理,空間釋放的類模板。

容器分類:

  1. 序列容器 sequence containers

    • array
    • vector
    • deque
    • list
    • forward-list
  2. 關(guān)聯(lián)容器 associative containers

    (紅黑樹實(shí)現(xiàn))

    • set
    • multiset
    • map
    • multimap
  3. 無序容器 (哈希表實(shí)現(xiàn))

    • unordered_map
    • unordered_multimap
    • unordered_set
    • unordered_multiset
  4. 支持隨機(jī)訪問的容器:string, array, vector, deque

    支持在任意位置插入 / 刪除的容器:list, forward_list

    支持在尾部插入元素:vector, string, deque

優(yōu)先隊(duì)列 priority_queue

? 每次彈出優(yōu)先級(jí)最高的元素,默認(rèn)是大頂堆(less<int>),即最大的元素優(yōu)先級(jí)最高,我們也可以傳入 greater<int>使之變?yōu)樾№敹?,此時(shí)最小的元素優(yōu)先級(jí)最高,優(yōu)先彈出最小的元素。第三個(gè)類型參數(shù)(可調(diào)用對(duì)象)是和堆的優(yōu)先級(jí)反著來的。

容器操作使迭代器(指針、引用)失效問題

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-AXxOoHbQ-1678759203347)(C:\Users\86130\AppData\Roaming\Typora\typora-user-images\image-20230303102111687.png)]

數(shù)組 和 鏈表對(duì)比

  • 存儲(chǔ)方式:數(shù)組在內(nèi)存中連續(xù)存儲(chǔ),且大小固定;鏈表在內(nèi)存中分散存儲(chǔ),大小不固定,可隨時(shí)拓展。存儲(chǔ)同樣的元素鏈表更占用空間,因?yàn)槌嗽乇旧硪酝猓湵淼拿總€(gè)節(jié)點(diǎn)還要存儲(chǔ)指向下一個(gè)節(jié)點(diǎn)的指針。
  • 隨機(jī)訪問:數(shù)組有下表操作,隨機(jī)訪問時(shí)間復(fù)雜度是O(1),而鏈表要從頭節(jié)點(diǎn)開始遍歷直到找到為止,時(shí)間復(fù)雜度是O(n)的
  • 頭部插入刪除:數(shù)組在頭部插入和刪除都是O(n)的,因?yàn)橐阉性匾来瓮笈惨粋€(gè)位置,才能把第一個(gè)位置空出來。而鏈表的頭插和頭刪都是O(1)的,可以使用頭指針在頭部進(jìn)行插入和刪除。
  • 尾部插入刪除:數(shù)組在尾部插入和刪除都是O(1)的,因?yàn)橛邢聵?biāo)操作,可以直接定位到尾部進(jìn)行刪除。鏈表如果提前維護(hù)了尾指針的話可以利用尾指針做到O(1)的時(shí)間復(fù)雜度進(jìn)行尾插和尾刪,如果沒有提前維護(hù)尾指針,就要從頭結(jié)點(diǎn)遍歷先找到尾節(jié)點(diǎn),才能進(jìn)行插入和刪除操作。

rezise 和 reserve 的區(qū)別

  • resize 調(diào)整容器長度大小,reserve 只是預(yù)分配空間,不改變?nèi)萜鲗?shí)際大小
  • resize 會(huì)創(chuàng)建或刪除元素,而 reserve 不會(huì)

為什么c++要有模板編程?

? 通過模板編程可以實(shí)現(xiàn)通用的容器和算法,比如STL 和 Boost庫等。模板對(duì)類型有很強(qiáng)的抽象能力,可以讓容器和算法更加通用。模板編程在一些大型項(xiàng)目里也有利于寫出高復(fù)用性的代碼。

什么是函數(shù)指針?

函數(shù)指針就是指向函數(shù)的指針變量,每一個(gè)函數(shù)都有一個(gè)入口地址,該入口地址就是函數(shù)指針?biāo)赶虻牡刂贰?/p>

nullptr可以調(diào)用成員函數(shù)嗎?為什么?

? 能。因?yàn)樵诰幾g時(shí)對(duì)象就綁定了成員函數(shù)地址,和指針空不空無關(guān),指針為空只代表 this 對(duì)象的指針為空,無法訪問成員變量。編譯時(shí),成員函數(shù)的地址就和指針綁定,所以可以調(diào)用成員函數(shù),如果成員函數(shù)中沒有使用到 this 指針,那么就不會(huì)報(bào)錯(cuò),如果用到了 this 指針,就會(huì)應(yīng)為 this 指針是 nullptr 而報(bào)錯(cuò)。

什么是野指針?怎么產(chǎn)生的?如何避免?

? 就是指向的位置無法確定的指針。

? 主要是釋放內(nèi)存后指針不及時(shí)置空,任然指向原來的地方,就會(huì)出現(xiàn)非法訪問的錯(cuò)誤。

避免辦法:

  • 初始化置 nullptr
  • 釋放內(nèi)存后置 nullptr
  • 申請(qǐng)后判空
  • 使用智能指針

內(nèi)聯(lián)函數(shù)

? 內(nèi)聯(lián)函數(shù)本質(zhì)是一個(gè)函數(shù),內(nèi)聯(lián)函數(shù)會(huì)在編譯時(shí)進(jìn)行代碼插入,編譯器會(huì)在沒出調(diào)用內(nèi)聯(lián)函數(shù)的地方直接把內(nèi)聯(lián)函數(shù)的內(nèi)容展開,這樣可以省去函數(shù)的調(diào)用開銷,提高效率,因此,內(nèi)聯(lián)函數(shù)中不能包含復(fù)雜的控制語句,否則,如果執(zhí)行函數(shù)體內(nèi)代碼的時(shí)間比函數(shù)調(diào)用的開銷大,那么效率可能會(huì)得到負(fù)提升,這就沒有使用內(nèi)聯(lián)函數(shù)的必要了。

宏函數(shù) 和 內(nèi)聯(lián)函數(shù) 的區(qū)別

  • 宏定義不是函數(shù)。而內(nèi)聯(lián)函數(shù)滿足函數(shù)的性質(zhì),有返回值和參數(shù)列表,是一個(gè)函數(shù)。
  • 宏函數(shù)是編譯時(shí)把所有宏名用宏體替換,簡單來說是字符串替換;而內(nèi)聯(lián)函數(shù)是在編譯時(shí)執(zhí)行代碼插入,編譯器會(huì)在調(diào)用內(nèi)聯(lián)函數(shù)的地方直接把內(nèi)聯(lián)函數(shù)展開,省去了函數(shù)的調(diào)用開銷,提高效率。
  • 宏定義沒有類型檢查,無論對(duì)錯(cuò)都直接替換;而內(nèi)聯(lián)函數(shù)在編譯時(shí)進(jìn)行類型檢查

宏定義和typedef區(qū)別?

  • 宏主要用于定義常量及書寫復(fù)雜的內(nèi)容;typedef用于定義類型別名。
  • 宏替換發(fā)生在預(yù)處理,屬于文本插入替換;typedef是編譯的一部分。
  • 宏不檢查類型;typedef會(huì)檢查數(shù)據(jù)類型。
  • 宏不是語句,不在在最后加分號(hào);typedef是語句,要加分號(hào)標(biāo)識(shí)結(jié)束。

內(nèi)聯(lián)函數(shù)和普通函數(shù)的區(qū)別

  • 內(nèi)聯(lián)函數(shù)比普通函數(shù)多了 inline 關(guān)鍵字
  • 內(nèi)聯(lián)函數(shù)在編譯時(shí)執(zhí)行代碼插入,省去了函數(shù)調(diào)用的開銷。
  • 普通函數(shù)被調(diào)用時(shí)需要尋址,內(nèi)聯(lián)函數(shù)不需要尋址
  • 內(nèi)聯(lián)函數(shù)的函數(shù)體不能包含復(fù)雜的控制語句,普通函數(shù)沒有這個(gè)要求。

static 的作用

  • 不考慮類的情況
    • 隱藏。所有不加static的全局變量和函數(shù)具有全局可見性,可以在其他文件中使用,加了之后只能在該文件所在的編譯模塊中使用
    • 默認(rèn)初始化為0,包括未初始化的全局靜態(tài)變量與局部靜態(tài)變量,都存在全局未初始化區(qū)
    • 靜態(tài)變量在函數(shù)內(nèi)定義,始終存在,且只進(jìn)行一次初始化,具有記憶性,其作用范圍與局部變量相同,函數(shù)退出后仍然存在,但不能使用
  • 考慮類的情況
    • static成員變量:只與類關(guān)聯(lián),不與類的對(duì)象關(guān)聯(lián)。定義時(shí)要分配空間,不能在類聲明中初始化,必須在類定義體外部初始化,初始化時(shí)不需要標(biāo)示為static;可以被非static成員函數(shù)任意訪問。
    • static成員函數(shù):不具有this指針,無法訪問類對(duì)象的非static成員變量和非static成員函數(shù);不能被聲明為const、虛函數(shù)和volatile;可以被非static成員函數(shù)任意訪問

const 和 define 的區(qū)別?

? const 定義只讀常量,define 定義宏,二者都可用于定義常量,但是有區(qū)別。

  • const 生效于編譯階段,define 生效于預(yù)處理階段
  • const 定義的常量存儲(chǔ)在內(nèi)存,需要額外內(nèi)存空間;define 定義的常量,運(yùn)行時(shí)是直接的操作數(shù),不在內(nèi)存中
  • const 定義的常量帶類型;define 定義的常量不帶類型,因此不利于類型檢查

strlen和sizeof區(qū)別?

  • sizeof是運(yùn)算符,不是函數(shù),編譯時(shí)就能獲得結(jié)果;strlen是字符處理的庫函數(shù),運(yùn)行時(shí)才有結(jié)果。
  • sizeof參數(shù)可以是任何數(shù)據(jù)的類型或者數(shù)據(jù);strlen的參數(shù)只能是字符指針且結(jié)尾是’\0’的字符串。

OOP 思想

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-K6HuF4gq-1678759203348)(C:\Users\86130\AppData\Roaming\Typora\typora-user-images\image-20230310100939220.png)]

多態(tài)如何實(shí)現(xiàn)的

? C++ 中多態(tài)有兩種類型,靜態(tài)多態(tài) 和 動(dòng)態(tài)多態(tài)。

? 靜態(tài)多態(tài)通過函數(shù)重載實(shí)現(xiàn)。編譯時(shí)編譯器會(huì)根據(jù)參數(shù)判斷調(diào)用的到底是重載函數(shù)的哪個(gè)版本。動(dòng)態(tài)多態(tài)通過虛函數(shù)實(shí)現(xiàn)。

虛函數(shù)(動(dòng)態(tài)多態(tài)是如何實(shí)現(xiàn)的)

? 當(dāng)?個(gè)類中包含虛函數(shù)時(shí),編譯器會(huì)為該類?成?個(gè)虛函數(shù)表,保存該類中虛函數(shù)的地址,同樣,派?類繼承基類,派?類中?然?定有虛函數(shù),所以編譯器也會(huì)為派?類?成??的虛函數(shù)表。當(dāng)我們定義?個(gè)派?類對(duì)象時(shí),編譯器檢測該類型有虛函數(shù),所以為這個(gè)派?類對(duì)象?成?個(gè)虛函數(shù)指針,指向該類型的虛函數(shù)表,這個(gè)虛函數(shù)指針的初始化是在構(gòu)造函數(shù)中完成的。

? 后續(xù)如果有?個(gè)基類類型的指針,指向派?類,那么當(dāng)調(diào)?虛函數(shù)時(shí),就會(huì)根據(jù)所指真正對(duì)象的虛函數(shù)表指針去尋找虛函數(shù)的地址,就可以調(diào)?派?類虛函數(shù)表中的虛函數(shù),以此實(shí)現(xiàn)多態(tài)。

構(gòu)造函數(shù)能不能是虛函數(shù)?為什么?

? 不能。一個(gè)類中所有虛函數(shù)的地址都記錄在虛函數(shù)表中,類的每個(gè)對(duì)象都有一個(gè)虛函數(shù)表指針指向該類的虛函數(shù)表,進(jìn)而能夠調(diào)用相應(yīng)的虛函數(shù)。調(diào)用虛構(gòu)造函數(shù)需要虛表指針,但是虛表指針是在構(gòu)造函數(shù)中初始化的,如果構(gòu)造函數(shù)是虛函數(shù)就無法初始化虛表指針。

父類的析構(gòu)函數(shù)為什么必須是虛函數(shù)?

? 如果不是虛函數(shù)的話,指針對(duì)象在析構(gòu)時(shí)就無法觸發(fā)多態(tài),只會(huì)調(diào)用父類的析構(gòu)函數(shù),子類的析構(gòu)函數(shù)沒有被調(diào)用,造成內(nèi)存泄漏的情況。

抽象類

? 抽象類是指含有純虛函數(shù)的類,純虛函數(shù)沒有自己的實(shí)現(xiàn),定義純虛函數(shù)是為了定義一個(gè)接口,起到強(qiáng)制規(guī)范的作用,規(guī)范抽象類的子類必須實(shí)現(xiàn)這個(gè)函數(shù),否則子類也是一個(gè)抽象類。

? 抽象類不能實(shí)例化對(duì)象,不僅僅因?yàn)樗募兲摵瘮?shù)沒有實(shí)現(xiàn),也是因?yàn)樵诖蠖鄶?shù)情況下,基類本身生成對(duì)象是不合理的。例如動(dòng)物作為基類可以派生出老虎、獅子等子類,但動(dòng)物本身生成對(duì)象明顯不合理。所以動(dòng)物類應(yīng)該被定義為抽象類,只定義一些純虛函數(shù)作為接口,強(qiáng)制子類實(shí)現(xiàn)這些接口。

公有、私有、受保護(hù)繼承

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-u0SpigTj-1678759203349)(C:\Users\86130\AppData\Roaming\Typora\typora-user-images\image-20230302124259702.png)]

重載和重寫(覆蓋)的區(qū)別

  • 重寫是父類和子類之間的垂直關(guān)系,重載是不同函數(shù)之間的水平關(guān)系
  • 重寫要求參數(shù)列表相同,重載則要求參數(shù)列表不同,返回值不要求
  • 重寫中,實(shí)際調(diào)用方法根據(jù)對(duì)象類型決定,重載根據(jù)調(diào)用時(shí)實(shí)參表與形參表的對(duì)應(yīng)關(guān)系來選擇函數(shù)體

什么是隱藏?

隱藏指的是,派生類中的函數(shù)屏蔽了基類中的同名函數(shù),對(duì)參數(shù)列表沒有要求,可以相同也可以不同。

淺拷貝和深拷貝的區(qū)別

**淺拷貝:**淺拷貝只是拷貝一個(gè)指針,并沒有新開辟一個(gè)地址,拷貝的指針和原來的指針指向同一塊地址,如果原來的指針?biāo)赶虻馁Y源釋放了,那么再釋放淺拷貝的指針的資源就會(huì)出現(xiàn)錯(cuò)誤。

**深拷貝:**深拷貝不僅拷貝值,還開辟出一塊新的空間用來存放新的值,即使原先的對(duì)象被析構(gòu)掉,釋放內(nèi)存了也不會(huì)影響到深拷貝得到的值。在自己實(shí)現(xiàn)拷貝賦值的時(shí)候,如果有指針變量的話是需要自己實(shí)現(xiàn)深拷貝的。

操作系統(tǒng)

什么是進(jìn)程

? 進(jìn)程是系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序,程序一旦運(yùn)行就是一個(gè)進(jìn)程。進(jìn)程是系統(tǒng)進(jìn)行資源分配的最小單位。每個(gè)進(jìn)程擁有獨(dú)立的地址空間。所以一個(gè)進(jìn)程無法直接訪問另一個(gè)進(jìn)程的變量和數(shù)據(jù)結(jié)構(gòu)。想訪問的話要使用進(jìn)程間通信,比如管道消息隊(duì)列等。

  • **孤兒進(jìn)程:**父進(jìn)程退出后,其子進(jìn)程還在運(yùn)行,他們就是孤兒進(jìn)程,他們會(huì)被 init 進(jìn)程收養(yǎng),并由 init 進(jìn)程完成狀態(tài)收集工作。
  • **僵尸進(jìn)程:**一個(gè)進(jìn)程使用 fork 函數(shù)創(chuàng)建子進(jìn)程,子進(jìn)程退出后,沒調(diào)用 wait() 獲取子進(jìn)程的終止?fàn)顟B(tài),但子進(jìn)程的描述符仍然保存在系統(tǒng)中占用資源。(使用 kill 命令解決僵尸進(jìn)程)

進(jìn)程與線程的區(qū)別

  • 進(jìn)程是操作系統(tǒng)資源分配的最小單位,線程是 CPU 調(diào)度的最小單位。
  • 進(jìn)程擁有完整獨(dú)立的內(nèi)存單元,而線程只有必不可少的資源,比如寄存器和棧
  • 一個(gè)線程獨(dú)屬于一個(gè)進(jìn)程,一個(gè)進(jìn)程可以包含多個(gè)線程
  • 進(jìn)程上下文切換的開銷遠(yuǎn)大于線程,很影響系統(tǒng)性能。
  • 進(jìn)程間的信息難以共享,必須使用進(jìn)程間通信來共享信息。而多個(gè)線程共享進(jìn)程的內(nèi)存。

進(jìn)程間通信

① 和 ② 管道有兩種

? 前兩種分別是有名管道 pipe 和 無名管道 FIFO,他們唯一的區(qū)別就是:無名管道只能進(jìn)行相關(guān)聯(lián)進(jìn)程之間的通信,比如父子進(jìn)程。而有名管道可以進(jìn)行任何進(jìn)程之間的通信。

? 管道類似一個(gè)隊(duì)列,管道里的數(shù)據(jù)是先進(jìn)先出的,而且是半雙工的。這意味著同一時(shí)間管道里的數(shù)據(jù)只能往一個(gè)方向流動(dòng)。管道的實(shí)質(zhì)就是在內(nèi)核中創(chuàng)建一個(gè)緩沖區(qū),一端的進(jìn)程寫入數(shù)據(jù),另一端的進(jìn)程讀取數(shù)據(jù)。

? 由于管道通過內(nèi)核交換數(shù)據(jù),因此通信效率很低,不適合頻繁交換數(shù)據(jù)。

③ 消息隊(duì)列

? 消息隊(duì)列本質(zhì)是保存在內(nèi)核中的鏈表,由多個(gè)獨(dú)立數(shù)據(jù)塊組成。與管道相比消息隊(duì)列不一定按照先進(jìn)先出的方式讀取,可以按消息類型讀取。

? 消息隊(duì)列的生命周期與內(nèi)核相關(guān)而與進(jìn)程無關(guān),如果不顯式的刪除,那消息隊(duì)列就會(huì)一直存在。

? 消息隊(duì)列無法實(shí)現(xiàn)實(shí)時(shí)通信,數(shù)據(jù)塊有大小限制,而且消息隊(duì)列通信過程中,存在用戶態(tài)與內(nèi)核態(tài)之間的拷貝開銷。

④ 共享內(nèi)存

? 共享內(nèi)存用來解決用戶態(tài)和內(nèi)核態(tài)之間頻繁的發(fā)生拷貝?,F(xiàn)代操作系統(tǒng)普遍采用虛擬內(nèi)存進(jìn)行內(nèi)存管理,每個(gè)進(jìn)程有自己獨(dú)立的虛擬內(nèi)存空間,不同進(jìn)程的虛擬內(nèi)存空間映射到不同的物理內(nèi)存。共享內(nèi)存就是拿出一塊虛擬地址空間,映射到同一塊物理內(nèi)存。好處是一個(gè)進(jìn)程寫入數(shù)據(jù)后另一個(gè)進(jìn)程可以立即查看,而且不用拷貝,效率很高。所以這是進(jìn)程間最快的通信方式。

? 由于多個(gè)進(jìn)程都可以操作共享內(nèi)存,所以需要同步對(duì)共享內(nèi)存的讀寫。

⑤ 信號(hào)量

? 它本質(zhì)是一個(gè)計(jì)數(shù)器,表示的是資源的數(shù)量。它用于實(shí)現(xiàn)進(jìn)程間的互斥與同步。當(dāng)進(jìn)程間使用共享內(nèi)存通信時(shí)就要用信號(hào)量來同步數(shù)據(jù)的讀寫。

? 信號(hào)量有兩個(gè)操作,P操作占用資源,會(huì)把信號(hào)量 -1,-1 之后如果信號(hào)量 < 0,就表示資源已被占用,其他進(jìn)程要阻塞等待。如果 -1 之后還 >= 0 表明還剩余資源,進(jìn)程正常執(zhí)行。V 操作和 P 操作相反,他會(huì)釋放資源,使信號(hào)量 + 1。一個(gè)進(jìn)程使用完資源后會(huì)執(zhí)行 V 操作,使信號(hào)量 + 1 以便其他進(jìn)程訪問資源。

信號(hào)量 與 互斥量 之間的區(qū)別:

互斥量用于線程互斥,信號(hào)量用于線程同步。

互斥同步 的區(qū)別:

  • 互斥指某資源同時(shí)只允許一個(gè)訪問者對(duì)其進(jìn)行訪問,具有唯一性和排他性。但互斥不能限制訪問者對(duì)資源的訪問順序,也就是說訪問是無序的。
  • 而同步可以在互斥的基礎(chǔ)上,實(shí)現(xiàn)訪問者對(duì)資源的有序訪問。

⑥ 信號(hào)

? 可以在任何時(shí)刻給進(jìn)程發(fā)送信號(hào)。收到信號(hào)的進(jìn)程可以執(zhí)行信號(hào)的默認(rèn)操作,或是自定義信號(hào)處理函數(shù),也可以直接忽略信號(hào)。

⑦ socket

? 實(shí)現(xiàn)不同主機(jī)上進(jìn)程的通信。

你是怎么使用的?

? 我用的最多的還是 socket ,進(jìn)行不同主機(jī)上進(jìn)程的通信。最直接的使用過程就是,客戶端和服務(wù)端都創(chuàng)建 socket 并綁定 IP 和端口號(hào)作為網(wǎng)絡(luò)通信的接口,然后服務(wù)器阻塞地 linten 等待客戶端連接,客戶端通過 connect 連接服務(wù)端,服務(wù)端地 accept 被觸發(fā),然后就可以通過 read、write 互相收發(fā)數(shù)據(jù)了。

進(jìn)程間通信可以用互斥鎖嗎?

? 可以是可以就是有點(diǎn)復(fù)雜,需要使用共享內(nèi)存。開辟一塊共享內(nèi)存,使得要通信的進(jìn)程可以訪問同一塊區(qū)域,然后把互斥鎖定義在共享內(nèi)存上,使相關(guān)進(jìn)程都可以使用該鎖。初始化該鎖的時(shí)候,設(shè)置為進(jìn)程間共享,這樣兩個(gè)進(jìn)程連接到共享內(nèi)存后,就都可以獲得該互斥鎖。

? 如果是不同主機(jī)上的進(jìn)程間通信就要用分布式鎖了。

線程間通信

  • **互斥鎖:**只有擁有鎖的線程才能訪問,保證了公共資源不被多個(gè)線程同時(shí)訪問。
  • **信號(hào)量:**本質(zhì)是個(gè)計(jì)數(shù)器,實(shí)現(xiàn)了多個(gè)線程對(duì)同一資源的有序訪問。
  • **條件變量:**通過條件變量通知的操作保持多線程同步。
  • **讀寫鎖:**與互斥鎖類似,不過讀寫鎖允許同一時(shí)間只有一個(gè)線程寫,可以有多個(gè)線程讀,效率比互斥鎖高。

條件變量

? 條件變量本質(zhì)是一個(gè)全局變量,它的功能是阻塞線程,直到接收到“條件成立”的信號(hào)后,被阻塞的線程才能繼續(xù)執(zhí)行。一個(gè)條件變量可以阻塞多個(gè)線程,當(dāng)條件成立時(shí),條件變量可以解除線程的“被阻塞狀態(tài)”。

? 使用條件變量時(shí)要借助一把互斥鎖共同完成功能。用條件變量阻塞某線程之前必須先對(duì)互斥鎖完成“加鎖”操作,然后條件變量就會(huì)阻塞線程,直到收到“條件成立的信號(hào)”,當(dāng)線程被添加到等待隊(duì)列上后,會(huì)自動(dòng)將剛剛的互斥鎖“解鎖”。當(dāng)其他線程發(fā)來“條件成立”信號(hào)后,條件變量不會(huì)立即結(jié)束對(duì)當(dāng)前線程的阻塞,而是先完成對(duì)互斥鎖的“加鎖”操作,然后再解除阻塞。

? 我在設(shè)計(jì) RPC 框架的日志模塊時(shí)使用到了條件變量。我用一個(gè)隊(duì)列存儲(chǔ)各個(gè) worker 線程的日志信息,最后由單獨(dú)的線程將隊(duì)列中的數(shù)據(jù)寫入磁盤。各個(gè)線程通過條件變量進(jìn)行互斥和同步。當(dāng)隊(duì)列為空時(shí),磁盤 IO 線程被條件變量阻塞,當(dāng) worker 線程向日志隊(duì)列寫入日志后,會(huì)通知磁盤 IO 線程條件成立,此時(shí)該線程就會(huì)將隊(duì)列中的日志數(shù)據(jù)寫入磁盤。

進(jìn)程狀態(tài)轉(zhuǎn)換

image-20230305104746111

死鎖

? 是什么:死鎖是指多個(gè)進(jìn)程循環(huán)等待別人占有的資源而無限期僵持下去的情況。

形成的必要條件

  • ① 互斥。某個(gè)資源同時(shí)只允許一個(gè)進(jìn)程訪問。如果已經(jīng)有進(jìn)程訪問該資源,其他線程就不能訪問,直到該進(jìn)程訪問結(jié)束。
  • ② 請(qǐng)求保持(占有的同時(shí)還在等待)。一個(gè)進(jìn)程占有一部分資源的同時(shí),還有資源未得到,需要其他進(jìn)程釋放該資源。
  • ③ 不可搶占。別的進(jìn)程已經(jīng)占有某種資源,自己不能去搶。
  • ④ 循環(huán)等待。存在一個(gè)循環(huán),每個(gè)進(jìn)程都需要其他進(jìn)程的資源,但每個(gè)進(jìn)程又因?yàn)槿鄙偎栀Y源而不能繼續(xù)運(yùn)行。

避免死鎖的方法:

? 既然死鎖形成有四個(gè)必要條件,那么我們避免四個(gè)條件同時(shí)產(chǎn)生就行了。其中 互斥條件 是必須的,所以具體有三個(gè)辦法:

  • ① 破壞“占有的同時(shí)還在等”:資源一次性分配。用完資源就釋放。
  • ② 破壞“不可搶占”:當(dāng)進(jìn)程想要的資源在其他進(jìn)程手里時(shí),手里的資源就別占著了,先把已經(jīng)獲取到的資源也釋放掉,供其他進(jìn)程使用。
  • ③ 破壞“循環(huán)等待:實(shí)現(xiàn)資源的有序分配,所有進(jìn)程申請(qǐng)資源必須按照順序。

協(xié)程是什么

? 協(xié)程比線程更輕量級(jí),開銷遠(yuǎn)小于線程。就像一個(gè)進(jìn)程可以擁有多個(gè)線程一樣,一個(gè)線程也可以有多個(gè)協(xié)程;協(xié)程不被操作系統(tǒng)內(nèi)核管理,完全由程序控制。

? 協(xié)程擁有自己寄存器上下文和棧。協(xié)程調(diào)度切換時(shí),將寄存器上下文和棧保存到其他地方,切換回來的時(shí)候,再將他們恢復(fù)回來。每個(gè)協(xié)程與其他協(xié)程共享全局?jǐn)?shù)據(jù)。

協(xié)程和線程的區(qū)別是什么?

  • 線程和進(jìn)程都是同步機(jī)制,而協(xié)程是異步。
  • 協(xié)程能保留上一次調(diào)用時(shí)的狀態(tài)
  • 線程是搶占式,協(xié)程是非搶占式,需要程序員自己釋放使用權(quán)來切換到其他協(xié)程,同一時(shí)間只能有一個(gè)協(xié)程運(yùn)行。

LRU 算法

? LRU 算法一般使用鏈表作為數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn),鏈表的每個(gè)節(jié)點(diǎn)存儲(chǔ)緩存的數(shù)據(jù)。鏈表頭部數(shù)據(jù)是最近使用的,而末尾的數(shù)據(jù)是最久沒被使用的。當(dāng)內(nèi)存空間不夠時(shí),就從鏈表末尾淘汰最久沒使用的節(jié)點(diǎn),從而騰出內(nèi)存空間。

? 傳統(tǒng)的 LRU 算法是這樣的,當(dāng)訪問的頁在內(nèi)存里,就直接把該頁對(duì)應(yīng)的鏈表節(jié)點(diǎn)移動(dòng)到鏈表頭;當(dāng)訪問的頁不在內(nèi)存時(shí),就要把該頁放入到鏈表頭,并淘汰掉鏈表末尾的頁。

? 傳統(tǒng) LRU 算法存在預(yù)讀失效和緩存污染導(dǎo)致的緩存命中率下降問題,這會(huì)大大增加磁盤 I/O 的次數(shù),耗費(fèi)極大的性能。

? 為了解決預(yù)讀失效帶來的損耗,Linux 實(shí)現(xiàn)了兩個(gè) LRU 鏈表,活躍 LRU 鏈表和非活躍 LRU 鏈表,真正要使用的頁放在活躍 LRU 鏈表,而預(yù)讀取的頁先放在非活躍 LRU 鏈表。

? 為了解決緩存污染帶來的損耗,Linux 會(huì)把第一次訪問的頁放入非活躍鏈表,防止他們擠占了常用頁的空間,當(dāng)他們第二次被訪問的時(shí)候才會(huì)進(jìn)入活躍鏈表。

零拷貝技術(shù)

? 零拷貝技術(shù)的目的就是為了減少在文件傳輸或數(shù)據(jù)拷貝過程中,CPU 參與拷貝的次數(shù),以及用戶態(tài)和內(nèi)核態(tài)之間切換的次數(shù)。

? Linux 內(nèi)核版本2.1引入 sendfile() 系統(tǒng)調(diào)用,它可以代替 read() 和 write() 這兩個(gè)系統(tǒng)調(diào)用,從而減少兩次用戶態(tài)和內(nèi)核態(tài)之間的切換。以網(wǎng)絡(luò)傳輸為例,應(yīng)用進(jìn)程調(diào)用 sendfile 后,會(huì)切換到內(nèi)核態(tài),CPU 通知 DMA 把磁盤數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū),然后再直接從內(nèi)核緩沖區(qū)拷貝到 socket 緩沖區(qū),不用再拷貝到用戶態(tài),最后由 DMA 將 socket 緩沖區(qū)的數(shù)據(jù)拷貝到網(wǎng)卡。整個(gè)過程中只有 2 次上下文切換,CPU 只參與一次數(shù)據(jù)拷貝。但這還不是完全的零拷貝。

? Linux 內(nèi)核 2.4 開始,對(duì)于網(wǎng)卡支持 SG-DMA 技術(shù)的情況下,CPU 可以完全不參與數(shù)據(jù)的拷貝工作。當(dāng)數(shù)據(jù)從 DMA 拷貝到內(nèi)核緩沖區(qū)后,SG-DMA 可直接把內(nèi)核緩沖中的數(shù)據(jù)拷貝到網(wǎng)卡,不需要再從內(nèi)核緩沖區(qū)拷貝到 socket 緩沖區(qū),減少了一次數(shù)據(jù)拷貝。

? 據(jù)我所知 kafka 和 nginx 的實(shí)現(xiàn)里都利用了零拷貝技術(shù)。

I/O 多路復(fù)用模型

select

? select 實(shí)現(xiàn)多路復(fù)用的方式是,把已連接的 Socket 都放到一個(gè)文件描述符集合,然后調(diào)用 select 函數(shù)把文件描述符集合拷貝到內(nèi)核,讓內(nèi)核檢查是否有網(wǎng)絡(luò)事件產(chǎn)生,檢查的方式很粗暴,就是遍歷文件描述符集合,當(dāng)檢查到有事件產(chǎn)生后,將此 Socket 標(biāo)記為可讀或可寫, 最后把整個(gè)文件描述符集合回用戶態(tài),然后用戶態(tài)還也要遍歷整個(gè)集合找到可讀或可寫的 Socket,再對(duì)其處理。對(duì)于 select 這種方式,需要進(jìn)行 2 次「遍歷」文件描述符集合,還會(huì)發(fā)生 2 次「拷貝」文件描述符集合,先從用戶空間拷貝到內(nèi)核空間,由內(nèi)核修改后,再拷貝到用戶空間中。

poll

? poll 和 select 并沒有太大的本質(zhì)區(qū)別,只不過 poll 使用鏈表存儲(chǔ)文件描述符,沒有最大連接數(shù)的限制。但他仍是線性結(jié)構(gòu),需要遍歷整個(gè)集合找到可讀或可寫的 socket,而且還是有大量用戶態(tài)到內(nèi)核態(tài)的拷貝。

epoll

? epoll 在內(nèi)核中維護(hù)一棵紅黑樹,紅黑樹的每個(gè)節(jié)點(diǎn)就是我們關(guān)注的一個(gè) socket,紅黑樹是個(gè)高效的數(shù)據(jù)結(jié)構(gòu),增刪改一般時(shí)間復(fù)雜度是 O(logn)。把需要監(jiān)控的 socket 通過 epoll_ctl() 函數(shù)加入內(nèi)核的紅黑樹里,這樣就省去了反復(fù)拷貝文件描述符集合的開銷。

? epoll 使用事件驅(qū)動(dòng)機(jī)制,內(nèi)核維護(hù)一個(gè)鏈表來記錄就緒事件,當(dāng)某個(gè) socket 有事件發(fā)生時(shí),通過回調(diào)函數(shù)將其加入到就緒事件列表,當(dāng)調(diào)用 epoll_wait() 函數(shù)時(shí),只會(huì)返回有事件發(fā)生的文件描述符,不用像 select/poll 那樣輪詢掃描整個(gè) socket 集合,大大提高了檢測的效率。

? epoll 有兩種工作模式。水平觸發(fā) 和 邊沿觸發(fā)。水平觸發(fā)就是當(dāng)epoll_wait檢測到某事件被觸發(fā)時(shí),若應(yīng)用程序不把數(shù)據(jù)全讀完,那么下次調(diào)用epoll_wait函數(shù)還會(huì)再次向應(yīng)用程序報(bào)告該事件,直到事件被處理。select 和 poll就只有這一種工作模式。邊沿觸發(fā)下,當(dāng)epoll_wait檢測到某事件被觸發(fā)時(shí),只會(huì)報(bào)告一次,直到下次再有新數(shù)據(jù)流入之前都不會(huì)在報(bào)告,無論文件描述符中是否還有數(shù)據(jù)可讀。所以只能一次性把數(shù)據(jù)讀完。這變向降低了同一事件被重復(fù)觸發(fā)的次數(shù),因此效率比select 和 poll高。

高性能網(wǎng)絡(luò)模式 reactor 和 proactor

?

說說大小字節(jié)序,用一個(gè)簡單函數(shù)判別大小序

  • 大端字節(jié)序:高位字節(jié)放在內(nèi)存的低地址端,低位字節(jié)放在內(nèi)存的高地址端
  • 小端字節(jié)序:低位字節(jié)放在內(nèi)存的低地址端,高位字節(jié)放在內(nèi)存的高地址端。
bool BigEndian()
{
	int x = 0x12345678;
    char *p = (char*)&x;
    if (*p == 0x78) return false;	// 小端序
    else return true;	// 大端序
}

計(jì)算機(jī)網(wǎng)絡(luò)

七層模型

? OIS 七層模型是由國際標(biāo)準(zhǔn)化組織指定的網(wǎng)絡(luò)七層模型。在實(shí)際中的應(yīng)?意義并不是很?,但是它對(duì)于理解?絡(luò)協(xié)議內(nèi)部的運(yùn)作很有幫助。它將計(jì)算機(jī)?絡(luò)體系結(jié)構(gòu)劃分為7層,每層都為上?層提供了良好的接??!?/p>

? 七層從上到下分別是:物理層、數(shù)據(jù)鏈路層、?絡(luò)層、傳輸層、會(huì)話層、表示層、應(yīng)?層。

  • 應(yīng)用層,負(fù)責(zé)給應(yīng)用程序提供統(tǒng)一的接口;(HTTP)
  • 表示層,負(fù)責(zé)把數(shù)據(jù)轉(zhuǎn)換成另一個(gè)系統(tǒng)能識(shí)別的格式;
  • 會(huì)話層,負(fù)責(zé)建立、管理和終止表示層實(shí)體之間的通信會(huì)話;
  • 傳輸層,負(fù)責(zé)端到端的數(shù)據(jù)傳輸;(TCP、UDP)
  • 網(wǎng)絡(luò)層,負(fù)責(zé)數(shù)據(jù)的路由、轉(zhuǎn)發(fā)、分片;(IP)
  • 數(shù)據(jù)鏈路層,負(fù)責(zé)把數(shù)據(jù)封裝成幀 和 差錯(cuò)檢測,以及 MAC 尋址;
  • 物理層,負(fù)責(zé)在物理網(wǎng)絡(luò)中傳輸數(shù)據(jù)幀;

TCP / IP 網(wǎng)絡(luò)模型有那幾層?

應(yīng)用層

? 應(yīng)用層為用戶提供應(yīng)用功能,不關(guān)心數(shù)據(jù)如何傳輸,工作在操作系統(tǒng)的用戶態(tài),他下面三層都工作在內(nèi)核態(tài)。

傳輸層

? 為應(yīng)用層提供網(wǎng)絡(luò)支持,把接收到的數(shù)據(jù)包根據(jù)端口號(hào)傳給應(yīng)用層應(yīng)用,或是使用 TCP / UDP 協(xié)議將應(yīng)用層傳下來的數(shù)據(jù)發(fā)送出去。

網(wǎng)絡(luò)層

? 網(wǎng)絡(luò)層通過 IP 協(xié)議將傳輸層的報(bào)文封裝為 IP 報(bào)文,通過 IP 地址唯一確定另外一個(gè)設(shè)備。

網(wǎng)絡(luò)接口層

? 網(wǎng)絡(luò)接口層在 IP 報(bào)文的前面加上 MAC 頭部,并封裝成數(shù)據(jù)幀發(fā)送到網(wǎng)絡(luò)上。網(wǎng)絡(luò)接口層使用 MAC 地址標(biāo)識(shí)網(wǎng)絡(luò)上的設(shè)備,將數(shù)據(jù)幀發(fā)送到另一臺(tái)設(shè)備的網(wǎng)絡(luò)接口層,再由它層層上報(bào),最終完成傳輸。

TCP為什么可靠(如何實(shí)現(xiàn)可靠傳輸)

? TCP通過檢驗(yàn)和、確認(rèn)應(yīng)答、超時(shí)重傳、連接管理、控制最大消息長度、流量控制、擁塞控制一起保證TCP傳輸?shù)目煽啃浴?/p>

  • **檢驗(yàn)和:**TCP把一個(gè)16位的字段作為校驗(yàn)和,發(fā)送方和接收方驗(yàn)證校驗(yàn)和是否相同。不相同則數(shù)據(jù)傳輸有誤,相同也可能有問題。
  • **確認(rèn)應(yīng)答:**ACK 和 序列號(hào)一應(yīng)一答機(jī)制能夠保證數(shù)據(jù)的完整性
  • **超時(shí)重傳:**發(fā)送數(shù)據(jù)包在一定時(shí)間周期內(nèi)沒有收到相應(yīng)的ACK,超時(shí)后就認(rèn)為該數(shù)據(jù)包丟失,發(fā)送方重新發(fā)送
  • **連接管理:**TCP通過三次握手,四次揮手實(shí)現(xiàn)穩(wěn)定的連接
  • **控制最大消息長度:**TCP會(huì)控制最大消息長度,理想的情況下數(shù)據(jù)剛好不被網(wǎng)絡(luò)層分塊,保證傳輸效率
  • **流量控制:**TCP用滑動(dòng)窗口控制發(fā)送方的發(fā)送速率,防止接收方來不及接收
  • **擁塞控制:**TCP剛開始發(fā)送數(shù)據(jù)很慢,先發(fā)送一點(diǎn)數(shù)據(jù)探測網(wǎng)絡(luò)是否擁塞,如果不擁塞了,就大量發(fā)送數(shù)據(jù)。如果突然擁塞,就又會(huì)很慢的發(fā)送數(shù)據(jù)。這樣避免由于網(wǎng)絡(luò)擁塞造成一系列問題

TCP 滑動(dòng)窗口是如何實(shí)現(xiàn)的?

? 發(fā)送方有發(fā)送窗口,接收方有接收窗口,兩個(gè)窗口的大小一般是一致的。發(fā)送窗口左邊的數(shù)據(jù)是已發(fā)送且收到 ACK 確認(rèn)的數(shù)據(jù),發(fā)送窗口內(nèi)的數(shù)據(jù)是在接收方處理范圍內(nèi)的最大數(shù)據(jù)量,他們有的已經(jīng)發(fā)送了,有的還未發(fā)送。當(dāng)未收到相應(yīng)數(shù)據(jù)的確認(rèn)時(shí),即便發(fā)送窗口的所有字節(jié)都發(fā)送出去了,發(fā)送窗口也不能移動(dòng),因?yàn)?TCP 要保證可靠傳輸。一旦收到 ACK 確認(rèn),滑動(dòng)窗口的左邊界就可以移動(dòng)到已收到確認(rèn)的最后一個(gè)字節(jié)序號(hào)對(duì)應(yīng)的位置。否則足夠長時(shí)間沒收到確認(rèn),就會(huì)觸發(fā)超時(shí)重傳。

? TCP 通過維護(hù)三個(gè)指針唯一確定發(fā)送窗口的狀態(tài)。指針 P1 指向發(fā)送窗口內(nèi)的第一個(gè)字節(jié)序號(hào),指針 P2 指向發(fā)送窗口內(nèi)已發(fā)送字節(jié)的下一個(gè)序號(hào)。指針 P3 指向發(fā)送窗口右側(cè)的下一個(gè)字節(jié)序號(hào)。
有了這三個(gè)指針就可以知道:

  • 小于 P1 的是已發(fā)送且已收到確認(rèn)的部分
  • 大于等于 P3 的是不允許發(fā)送的部分
  • P3 - P1 可以得到發(fā)送窗口的尺寸
  • P2 - P1 可以得到已發(fā)送但尚未收到確認(rèn)的字節(jié)數(shù)
  • P3 - P2 得到允許發(fā)送但當(dāng)前還沒發(fā)送的字節(jié)數(shù)

? 接收方確認(rèn)時(shí)有累計(jì)確認(rèn)和捎帶確認(rèn)機(jī)制,可以間接提升 TCP 傳輸?shù)男省?/p>

擁塞控制算法有哪些,怎么樣實(shí)現(xiàn)?

慢開始

? 連接建立完成后,初始化擁塞窗口=1,表示可以傳1個(gè) MSS 大小的數(shù)據(jù)。當(dāng)收到一個(gè) ACK 確認(rèn)后,擁塞窗口+1,此時(shí)能一次發(fā)送兩個(gè)。當(dāng)收到2個(gè) ACK 確認(rèn)后,擁塞窗口+2,此時(shí)一次發(fā)送4個(gè), 當(dāng)收到4個(gè) ACK 確認(rèn)后,擁塞窗口+4,此時(shí)能一次發(fā)送8個(gè)。以此類推,可以看出一次性發(fā)包的個(gè)數(shù)呈指數(shù)增長 。

? 這種增長不會(huì)無限持續(xù)下去,有一個(gè)慢啟動(dòng)門限,當(dāng)擁塞窗口大小 >= 慢啟動(dòng)門限時(shí)就會(huì)使用擁塞避免算法。

擁塞避免

? 假設(shè)慢啟動(dòng)門限就是8,那么當(dāng)收到 8 個(gè) ACK 確認(rèn)后,每個(gè)確認(rèn)只會(huì)為 擁塞窗口 增加 1 / 8,8個(gè)確認(rèn)就只增加1,此時(shí),一次能發(fā)送 9 個(gè) MSS 大小的數(shù)據(jù)??梢钥闯?,使用擁塞避免算法后,發(fā)包個(gè)數(shù)變成線性增長。

擁塞發(fā)生

? 當(dāng)網(wǎng)絡(luò)出現(xiàn)擁堵,會(huì)發(fā)生數(shù)據(jù)包重傳,重傳機(jī)制主要有兩種:超時(shí)重傳 和 快速重傳。發(fā)生這兩種情況使用的擁塞發(fā)生算法不一樣。

? 超時(shí)重傳是,當(dāng)網(wǎng)絡(luò)非常擁塞,發(fā)生超時(shí)重傳時(shí),會(huì)更新慢啟動(dòng)門限為擁塞窗口的一半,并重置擁塞窗口大小為1。然后重新開始慢啟動(dòng)算法。

? 快速重傳就是,當(dāng)接收方發(fā)現(xiàn)只丟了一個(gè)中間包的時(shí)候,發(fā)送三次對(duì)前一個(gè)包的 ACK ,發(fā)送端連續(xù)收到三個(gè) ACK 時(shí),就不必等待超時(shí)重傳。這種情況擁塞并不嚴(yán)重,不用再從起點(diǎn)執(zhí)行慢啟動(dòng)算法。而是會(huì)把擁塞窗口大小變?yōu)樵瓉淼囊话?,再讓慢啟?dòng)門限=擁塞窗口的大小。然后進(jìn)入快恢復(fù)算法。

快恢復(fù)

? 快恢復(fù)算法會(huì)先設(shè)置擁塞窗口大小為新的慢啟動(dòng)門限+3,然后重傳丟失的數(shù)據(jù)包,當(dāng)收到重復(fù)數(shù)據(jù)的 ACK 后,擁塞窗口+1,當(dāng)收到新數(shù)據(jù)的 ACK 后,把 擁塞窗口設(shè)置為慢啟動(dòng)門限的值,因?yàn)橐呀?jīng)接收到了新數(shù)據(jù)的 ACK,說明恢復(fù)過程已經(jīng)結(jié)束,可以再次進(jìn)入擁塞避免狀態(tài)。

如何提升 TCP 傳輸?shù)男剩?/h3>

? 發(fā)送端滑動(dòng)窗口定義了網(wǎng)絡(luò)中飛行報(bào)文的最大字節(jié)數(shù),當(dāng)它小于帶寬時(shí),就無法充分利用網(wǎng)絡(luò)帶寬時(shí)延積。要想提升發(fā)送速度必須提升滑動(dòng)窗口的上限,在 Linux 下是通過設(shè)置 tcp_window_scaling 為 1 做到的,此時(shí)最大值可達(dá)到 1GB。

時(shí)延帶寬積 = 傳播時(shí)延 * 帶寬

? 此外,內(nèi)核緩沖區(qū)也會(huì)決定滑動(dòng)窗口的上限,緩沖區(qū)分為:發(fā)送緩沖區(qū) 和 接收緩沖區(qū)??梢园丫彌_區(qū)的上限設(shè)置為帶寬時(shí)延積。然后設(shè)置發(fā)送緩沖區(qū)和接收緩沖區(qū)為自動(dòng)調(diào)節(jié)。這樣就既能最大程度地保持并發(fā)性,也能使得在系統(tǒng)資源充裕時(shí) 連接傳輸速度達(dá)到最大值。

TCP 和 UDP 的區(qū)別

  • TCP 是有連接的,在傳輸數(shù)據(jù)前必須通過三次握手建立連接。而 UDP 是無連接的,可以直接發(fā)送數(shù)據(jù)。
  • TCP 能保證數(shù)據(jù)按序發(fā)送,按序到達(dá),并提供超時(shí)重傳保證可靠性,而 UDP 提供不可靠服務(wù),只是努力交付數(shù)據(jù),不保證按序送到。
  • TCP 首部開銷大,最小20字節(jié),而 UDP 首部開銷小,只有 8 字節(jié)。
  • TCP 有流量控制和擁塞控制,UDP 沒有,網(wǎng)絡(luò)擁堵不會(huì)影響發(fā)送端的發(fā)送速率。
  • TCP 是一對(duì)一連接,而 UDP 可以支持一對(duì)一、一對(duì)多 和 多對(duì)多通信。
  • TCP 是面向字節(jié)流的服務(wù),而 UDP 對(duì)應(yīng)用層交付的報(bào)文直接打包。

TCP 連接

三次握手

? 連接前,客戶端和服務(wù)端都處于 CLOSE 狀態(tài),先是服務(wù)端主動(dòng)監(jiān)聽某個(gè)端口,處于 LISTEN 狀態(tài)。當(dāng)客戶端發(fā)起請(qǐng)求時(shí),會(huì)隨機(jī)初始化序列號(hào) client_isn,把該序列號(hào)至于 TCP 首部的序號(hào)字段中,同時(shí)把 SYN 標(biāo)志位置1。然后把該 SYN 報(bào)文發(fā)送給服務(wù)端,之后客戶端進(jìn)入 SYN_SENT 狀態(tài)。

? 服務(wù)端收到客戶端的 SYN 報(bào)文后,也隨機(jī)初始化自己的序號(hào) server_isn ,把該序號(hào)寫入 TCP 首部的序號(hào)字段中,然后在 TCP 首部的確認(rèn)應(yīng)答號(hào)字段填入 client_isn + 1,然后把 SYN 和 ACK 標(biāo)志位置1,然后就能發(fā)送了。發(fā)送后,服務(wù)端處于 SYN_RCVD 狀態(tài)。

? 客戶端收到報(bào)文后,還需發(fā)送一個(gè)應(yīng)答報(bào)文。先把 TCP 首部的 ACK 字段值1,然后在確認(rèn)應(yīng)答號(hào)字段寫入 server_isn + 1,然后就可以發(fā)送了。發(fā)送后客戶端轉(zhuǎn)為 ESTABLISHED 狀態(tài)。服務(wù)端收到應(yīng)答報(bào)文后也轉(zhuǎn)為 ESTABLISHED 狀態(tài)。

注:三次握手只有第三次,客戶端的應(yīng)答報(bào)文中可以攜帶數(shù)據(jù)。

為什么是三次?不是2次或4次?

  • 三次握手才可以阻止重復(fù)歷史連接的初始化(主要原因)
  • 三次握手才可以同步雙方的初始序列號(hào)
  • 三次握手才可以避免資源浪費(fèi)

為什么每次建立 TCP 連接時(shí),初始化的序列號(hào)都要求不一樣呢?

  • 為了防止歷史報(bào)文被下一個(gè)相同四元組的連接接收(主要方面);
  • 為了安全性,防止黑客偽造的相同序列號(hào)的 TCP 報(bào)文被對(duì)方接收;

四次揮手

c++八股文,c++,數(shù)據(jù)結(jié)構(gòu),算法

? 雙方都可以主動(dòng)斷開連接。以客戶端主動(dòng)斷開連接為例。

? 客戶端會(huì)發(fā)送 FIN 報(bào)文,TCP 首部的 FIN 標(biāo)志位置1,然后客戶端進(jìn)入 FIN_WAIT_1 狀態(tài)。

? 服務(wù)端接收到后會(huì)發(fā)送 ACK 應(yīng)答報(bào)文,并進(jìn)入 CLOSE_WAIT 狀態(tài)。

? 客戶端接收到 ACK 應(yīng)答報(bào)文后,進(jìn)入 FIN_WAIT_2 狀態(tài),繼續(xù)等待下一個(gè)報(bào)文。

? 服務(wù)端處理完數(shù)據(jù)后,也向客戶端發(fā)送 FIN 報(bào)文,并進(jìn)入 LAST_ACK 狀態(tài)。

? 客戶端收到 FIN 報(bào)文后,回復(fù)一個(gè) ACK 應(yīng)答報(bào)文,并進(jìn)入 TIME_WAIT 狀態(tài)。

? 服務(wù)端收到 ACK 應(yīng)答報(bào)文后,進(jìn)入 CLOSE 狀態(tài),至此服務(wù)端完成連接的關(guān)閉。

? 客戶端經(jīng)過一段時(shí)間后,自動(dòng)進(jìn)入 CLOSE 狀態(tài),至此客戶端也完成連接的關(guān)閉。

為什么揮手要四次?

  • 關(guān)閉連接時(shí),客戶端向服務(wù)端發(fā)送 FIN 時(shí),僅僅表示客戶端不再發(fā)送數(shù)據(jù)了但是還能接收數(shù)據(jù)。
  • 服務(wù)端收到客戶端的 FIN 報(bào)文時(shí),先回一個(gè) ACK 應(yīng)答報(bào)文,而服務(wù)端可能還有數(shù)據(jù)需要處理和發(fā)送,等服務(wù)端不再發(fā)送數(shù)據(jù)時(shí),才發(fā)送 FIN 報(bào)文給客戶端來表示同意現(xiàn)在關(guān)閉連接。

為什么 TIME_WAIT 等待的時(shí)間是 2倍的 MSL?TIME_WAIT 在哪個(gè)階段,會(huì)發(fā)生什么作用?

  • 防止歷史連接中的數(shù)據(jù),被后面相同四元組的連接錯(cuò)誤的接收。
  • 保證「被動(dòng)關(guān)閉連接」的一方,能被正確的關(guān)閉連接。如果被動(dòng)關(guān)閉方?jīng)]有收到斷開連接的最后的 ACK 報(bào)文,就會(huì)觸發(fā)超時(shí)重發(fā) FIN 報(bào)文,另一方接收到 FIN 后,會(huì)重發(fā) ACK 給被動(dòng)關(guān)閉方, 一來一去正好 2 個(gè) MSL。

HTTP 的報(bào)文結(jié)構(gòu)

? HTTP 報(bào)文由 報(bào)文首部 和 報(bào)文主體 組成。而報(bào)文首部又可分為 起始行 和 頭部,報(bào)文首部 和 報(bào)文主體之間通過一個(gè)空行分隔。

請(qǐng)求報(bào)文

? 請(qǐng)求報(bào)文的請(qǐng)求行描述了客戶端想要如何操作服務(wù)端的資源,它包括請(qǐng)求方法、請(qǐng)求目標(biāo)、版本號(hào)三部分。中間使用空格分隔,最后要用 CRLF 換行表示結(jié)束。

? 請(qǐng)求頭包含若干個(gè)屬性,格式為 “屬性名:屬性值”,主要用于說明請(qǐng)求源、連接類型以及Cookie等信息。

? 請(qǐng)求體就是 HTTP 要傳輸?shù)恼膬?nèi)容。

響應(yīng)報(bào)文

? 響應(yīng)報(bào)文的起始行由 HTTP 版本、狀態(tài)碼、狀態(tài)描述三個(gè)字段組成。狀態(tài)碼表示服務(wù)器處理此次 HTTP 請(qǐng)求的狀態(tài)。

? 響應(yīng)頭也是一個(gè)個(gè)鍵值對(duì),他們主要是一些服務(wù)器的基本信息,以及一些Cookie值。

? 響應(yīng)體就是服務(wù)器返回的具體數(shù)據(jù)。

HTTP 常見狀態(tài)碼(小林coding

https://www.xiaolincoding.com/network/2_http/http_interview.html#http-%E5%B8%B8%E8%A7%81%E7%9A%84%E7%8A%B6%E6%80%81%E7%A0%81%E6%9C%89%E5%93%AA%E4%BA%9B

HTTP 常見字段

  • **Host:**表示此次請(qǐng)求的服務(wù)器域名
  • **Content-Length:**表示此次回應(yīng)的數(shù)據(jù)長度
  • **Connection:**常用于客戶端要求服務(wù)器使用 HTTP 長連接機(jī)制,以便其他請(qǐng)求復(fù)用本次連接。
  • **Content-Type:**告知客戶端本次響應(yīng)的數(shù)據(jù)格式。
  • **Content-Encoding:**告知客戶端服務(wù)器返回的數(shù)據(jù)使用的壓縮格式。

TCP Keepalive 和 HTTP Keep-Alive 是一個(gè)東西嗎?

? HTTP 的 Keep-Alive 是由「應(yīng)用程序」實(shí)現(xiàn)的,目的是可以用同一個(gè) TCP 連接來發(fā)送和接收多個(gè) HTTP 的請(qǐng)求和應(yīng)答,減少了 HTTP 短連接帶來的多次 TCP 連接建立和釋放造成的開銷。

? TCP 的 Keepalive 是一種 ?;顧C(jī)制,由「內(nèi)核」實(shí)現(xiàn)的,當(dāng)客戶端和服務(wù)端長達(dá)一定時(shí)間沒交換過時(shí),內(nèi)核為了確認(rèn)該連接是否還有效,就會(huì)發(fā)送探測報(bào)文,來檢測對(duì)方是否還在線,從而決定是否要關(guān)閉該連接。

GET 和 POST 的區(qū)別

  • GET 是從服務(wù)器上請(qǐng)求數(shù)據(jù),POST 是向服務(wù)器提交數(shù)據(jù)。
  • GET 請(qǐng)求會(huì)被瀏覽器主動(dòng)緩存,留下歷史記錄,而 POST 默認(rèn)不會(huì)
  • GET 只能進(jìn)行 URL 編碼,只能接收 ASCII 字符,而 POST 沒有限制
  • GET 傳遞的數(shù)據(jù)以鍵值對(duì)的形式放在 URL 末尾,在網(wǎng)址中能直接看到,安全性差。而 POST 把請(qǐng)求參數(shù)放在請(qǐng)求體中,適合傳輸敏感數(shù)據(jù)。
  • GET 只讀取服務(wù)器的數(shù)據(jù),是冪等的,而 POST 執(zhí)行新增或提交數(shù)據(jù)的操作,可能修改服務(wù)器上的資源,所以不是冪等的。

介紹下 HTTPS

? HTTPS是一種能在網(wǎng)絡(luò)上進(jìn)行安全通信的傳輸協(xié)議,它通過 HTTP 進(jìn)行通信,利用 SSL/TLS 握手對(duì)數(shù)據(jù)包進(jìn)行加密。

HTTPS 建立連接的過程

? 首先客戶端和服務(wù)器進(jìn)行 TCP 三次握手建立連接。然后雙方要進(jìn)行 4 次 SSL/TLS 握手。第一次握手由客戶端發(fā)起加密通信請(qǐng)求,客戶端向服務(wù)器發(fā)送自己支持的 TLS 協(xié)議版本,還有支持的加密算法表,最重要的還有 生成的隨機(jī)數(shù)1,該隨機(jī)數(shù)用于生成會(huì)話密鑰。

? 服務(wù)端收到客戶端請(qǐng)求后,會(huì)確認(rèn)自己的 TLS 協(xié)議版本和加密算法,還會(huì)把數(shù)字證書也發(fā)給客戶端,而且也會(huì)生成隨機(jī)數(shù)2,也用于生成會(huì)話密鑰。

? 客戶端收到消息后,會(huì)先確認(rèn)數(shù)字證書的真實(shí)性,然后從中取出服務(wù)器的公鑰。然后告訴服務(wù)器后面的消息都要用會(huì)話密鑰加密,并表示客戶端的握手已經(jīng)結(jié)束了,此外客戶端還會(huì)生成隨機(jī)數(shù)3,一并發(fā)送給服務(wù)器。這整個(gè)報(bào)文會(huì)用剛剛得到的公鑰進(jìn)行加密。

? 至此客戶端和服務(wù)器各自都有了三個(gè)隨機(jī)數(shù),就能用剛剛商量好的加密算法生成本次通信的會(huì)話密鑰了。

? 服務(wù)器計(jì)算出會(huì)話密鑰后會(huì)通知客戶端,隨后的消息都用會(huì)話密鑰加密,并且也表示這是服務(wù)器的握手結(jié)束了。

至此,完成4次握手,客戶端與服務(wù)器進(jìn)入加密通信。

http 和 https 的區(qū)別

  • HTTP 是超文本傳輸協(xié)議,信息是明文傳輸,存在安全風(fēng)險(xiǎn)的問題。HTTPS 則解決 HTTP 不安全的缺陷,在 TCP 和 HTTP 網(wǎng)絡(luò)層之間加入了 SSL/TLS 安全協(xié)議,使得報(bào)文能夠加密傳輸。
  • HTTP 連接建立相對(duì)簡單, TCP 三次握手之后便可進(jìn)行 HTTP 的報(bào)文傳輸。而 HTTPS 在 TCP 三次握手之后,還需進(jìn)行 SSL/TLS 的握手過程,才可進(jìn)入加密報(bào)文傳輸。
  • 兩者的默認(rèn)端口不一樣,HTTP 默認(rèn)端口號(hào)是 80,HTTPS 默認(rèn)端口號(hào)是 443。
  • HTTPS 協(xié)議需要向 證書權(quán)威機(jī)構(gòu) 申請(qǐng)數(shù)字證書,來保證服務(wù)器的身份是可信的。

DNS 尋址過程

? 當(dāng)在瀏覽器中輸入某域名時(shí),瀏覽器先查自己的緩存中是否有該域名對(duì)應(yīng)的 IP 地址。沒有的話,操作系統(tǒng)會(huì)檢查本地 hosts 文件是否有該域名對(duì)應(yīng)的 IP。沒有就再到路由器緩存中查找。這三個(gè)查找過程都在本地完成,算本地 DNS 緩存。如果都沒找到就要使用互聯(lián)網(wǎng)服務(wù)提供商提供的 DNS 緩存了。這時(shí)有兩種解析策略 遞歸 和 迭代,兩種策略中,都要先訪問頂級(jí)域名服務(wù)器。

? 遞歸就是客戶端向本地 DNS 服務(wù)器發(fā)送 DNS 請(qǐng)求,如果本地 DNS 服務(wù)器中找不到,他會(huì)轉(zhuǎn)發(fā)給根域名服務(wù)器,根域名服務(wù)器收到請(qǐng)求后 解析域名后綴,然后轉(zhuǎn)發(fā)給相應(yīng)的頂級(jí)域名服務(wù)器,頂級(jí)域名服務(wù)器再轉(zhuǎn)發(fā)給權(quán)限域名服務(wù)器。遞歸過程中一旦找到 IP 就立刻向上返回,最終返回給客戶端。

? 而迭代查詢則是客戶端向本地 DNS 服務(wù)器發(fā)送 DNS 請(qǐng)求,如果本地 DNS 服務(wù)器中找不到,他會(huì)轉(zhuǎn)發(fā)給根域名服務(wù)器,根域名服務(wù)器收到請(qǐng)求后 解析域名后綴,把頂級(jí)域名服務(wù)器的 IP 告知本地域名服務(wù)器,由本地域名服務(wù)器自己去訪問頂級(jí)域名服務(wù)器,如果沒有,就把權(quán)限域名服務(wù)器的 IP 告知本地域名服務(wù)器,再由本地服務(wù)器自己去訪問權(quán)限域名服務(wù)器。最后把結(jié)果返回給客戶端。

從 url 輸入到顯示頁面發(fā)生了什么?

  • 首先瀏覽器對(duì) URL 進(jìn)行解析,從而生成發(fā)送給 Web 服務(wù)器的 HTTP 請(qǐng)求信息。

  • 發(fā)送前要確定 web 服務(wù)器的 IP 地址,瀏覽器先查看自身有沒有目標(biāo)域名的緩存,如果有就直接返回,否則要到 DNS 服務(wù)器查詢域名對(duì)應(yīng)的 IP。

  • 有了 IP 地址就可以把 HTTP 的傳輸工作交給操作系統(tǒng)中的協(xié)議棧。首先會(huì)進(jìn)行三次握手建立 TCP 連接,之后組裝好含有HTTP請(qǐng)求的 TCP 報(bào)文交給下面的網(wǎng)絡(luò)層處理。

    • ICMP 用于告知網(wǎng)絡(luò)包傳送過程中產(chǎn)生的錯(cuò)誤以及各種控制信息。
    • ARP 用于根據(jù) IP 地址查詢相應(yīng)的以太網(wǎng) MAC 地址。
  • 網(wǎng)絡(luò)層又把 TCP 報(bào)文封裝成 IP 報(bào)文,包含源地址 IP 和目標(biāo)地址 IP。

  • 接下來還要在 IP 頭部前面加上 MAC 頭部,它包含接收方和發(fā)送方的 MAC 地址等信息。

  • 經(jīng)過層層封裝,數(shù)據(jù)來到網(wǎng)卡,網(wǎng)卡將 MAC 幀傳為電信號(hào)通過網(wǎng)線發(fā)送出去。

  • 信號(hào)首先會(huì)到達(dá)交換機(jī),交換機(jī)根據(jù) MAC 地址表查找 MAC 地址,然后將信號(hào)發(fā)送到相應(yīng)的端口。

  • 經(jīng)過交換機(jī)后到達(dá)路由器,由路由器選擇一條網(wǎng)絡(luò)通路將數(shù)據(jù)轉(zhuǎn)發(fā)給另一個(gè)路由器。

  • 最終數(shù)據(jù)包抵達(dá)服務(wù)器,服務(wù)器先檢查數(shù)據(jù)包的 MAC 頭部,查看是否和自己的 MAC 地址符合,若符合繼續(xù)檢查 IP 頭是否符合,根據(jù)協(xié)議字段確定是 TCP 協(xié)議,然后根據(jù)端口號(hào)得知該數(shù)據(jù)包要轉(zhuǎn)發(fā)給 HTTP 進(jìn)程。HTTP 進(jìn)程將相應(yīng)數(shù)據(jù)封裝在 HTTP 響應(yīng)報(bào)文里,然后再經(jīng)過 TCP、IP、MAC 等協(xié)議的封裝,發(fā)回給客戶端??蛻舳耸盏?HTTP 響應(yīng)報(bào)文后,交給瀏覽器渲染頁面,這樣頁面就顯示出來了。

image-20230217201959927

MySQL數(shù)據(jù)庫

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-DVSm4BfD-1678759203349)(C:\Users\86130\AppData\Roaming\Typora\typora-user-images\image-20230224103719421.png)]

第一范式

數(shù)據(jù)庫的每一列都是不可分割的最小數(shù)據(jù)項(xiàng)單元。例如:地址:四川省宜賓市敘州區(qū)就不滿足第一范式,應(yīng)該拆分為:省份:四川省 城市:宜賓市 區(qū)域:敘州區(qū)

第二范式

在第一范式的基礎(chǔ)上,要求非主鍵字段完全依賴于主鍵(注:該主鍵不能是聯(lián)合主鍵)

第三范式

在第二范式的基礎(chǔ)上,要求非主鍵字段不依賴于其他非主鍵字段,消除傳遞依賴。例如:ID:1, name:張三, name_id:13 這里的name和name_id之間就存在傳遞依賴,不符合第三范式。

索引是什么?

? 索引是幫助 MySQL 高效獲取數(shù)據(jù)的一種數(shù)據(jù)結(jié)構(gòu),使用索引可以快速查表。如果不加索引的話,查某一行數(shù)據(jù)就要遍歷表的每行數(shù)據(jù),效率很低。如果有了索引,利用數(shù)據(jù)結(jié)構(gòu)就可以快速查找。

? 索引一般以文件的形式存儲(chǔ)在磁盤上,在對(duì)表進(jìn)行增刪改時(shí)數(shù)據(jù)庫也要不斷維護(hù)索引。

索引分類

單值索引:即一個(gè)索引只包含單個(gè)列,一個(gè)表可以有多個(gè)單值索引。(但不是越多越好,一般只對(duì)經(jīng)常查詢的字段建立索引)

唯一索引:索引列的值必須唯一,但允許有空值。

主鍵索引:設(shè)定為主鍵后會(huì)自動(dòng)建立索引。

復(fù)合索引:一個(gè)索引包含多個(gè)列。

事務(wù)的四大特性ACID

原子性:事務(wù)是不可分割的最小單位,一個(gè)事務(wù)內(nèi)的操作要么同時(shí)成功,要么同時(shí)失敗,不可再分。

一致性:事務(wù)執(zhí)行前后數(shù)據(jù)狀態(tài)應(yīng)該保持一致。例如轉(zhuǎn)賬前后雙方的賬戶總額應(yīng)該保持不變。

隔離性:并發(fā)執(zhí)行的兩個(gè)事務(wù)之間不會(huì)相互干擾。

持久性:一旦事務(wù)執(zhí)行成功,即便數(shù)據(jù)庫在此時(shí)崩潰了,在數(shù)據(jù)庫重啟時(shí)也能夠保證事務(wù)操作被持久化到數(shù)據(jù)庫中。

原子性和持久性是基于日志實(shí)現(xiàn)的,隔離性是通過鎖來實(shí)現(xiàn)的,三者做為基礎(chǔ)共同確保一致性的實(shí)現(xiàn)。

為什么InnoDB表必須有主鍵(沒有也會(huì)自動(dòng)將某一字段設(shè)為主鍵),并且盡量使用整型的自增主鍵?

  1. InnoDB的數(shù)據(jù)存儲(chǔ)本身就是在主鍵索引樹當(dāng)中,沒有主鍵就無法存儲(chǔ)數(shù)據(jù)?
  2. 整型的存儲(chǔ)空間比其它字段類型要小,并且查詢時(shí)要對(duì)主鍵進(jìn)行大量比較操作,整型數(shù)據(jù)的比較效率高于其它數(shù)據(jù)類型。
  3. 對(duì)于自增的主鍵而言,B+樹是有序的,插入數(shù)據(jù)與范圍查詢都非常方便

為什么InnoDB表的非主鍵索引結(jié)構(gòu)葉子結(jié)點(diǎn)存儲(chǔ)的是主鍵值?

  • 節(jié)約存儲(chǔ)空間。主鍵索引樹已經(jīng)存儲(chǔ)了完整的表記錄信息,無需再次存儲(chǔ)數(shù)據(jù)。
  • 其次是一致性考慮。非主鍵索引葉子節(jié)點(diǎn)統(tǒng)一去主鍵索引樹再查找,共享同一份數(shù)據(jù),就能確保一致性。
  • 最后是可以降低二級(jí)索引的維護(hù)成本。當(dāng)數(shù)據(jù)發(fā)生修改時(shí)只需要修改主鍵索引的葉子結(jié)點(diǎn),非主鍵索引就不用改了。

為什么是B+樹?

結(jié)合操作系統(tǒng)可以知道,數(shù)據(jù)庫索引的每一層向下查找一個(gè)節(jié)點(diǎn)都是一次獨(dú)立的磁盤 IO。而磁盤 IO 操作非常費(fèi)時(shí),因此要想辦法降低樹的高度。

首先考慮普通的二叉樹,在極端情況下二叉查找樹會(huì)變成一個(gè)單鏈表,而且各節(jié)點(diǎn)高度不穩(wěn)定,不如直接考慮平衡二叉樹。平衡二叉樹呢,雖然樹的高度得到了降低,但是一個(gè)節(jié)點(diǎn)只能存放一個(gè)關(guān)鍵字和記錄,考慮采用B樹。

B樹具有如下特點(diǎn):一個(gè)節(jié)點(diǎn)可以存儲(chǔ)多個(gè)關(guān)鍵字和記錄,所有索引關(guān)鍵字不會(huì)重復(fù)出現(xiàn),每個(gè)索引的關(guān)鍵字和記錄都存放在一起。但是B樹相比于B+樹也存在如下問題:

  1. 在B樹中越靠近根節(jié)點(diǎn)的記錄查找時(shí)間會(huì)更快,導(dǎo)致不同記錄的查詢效率不穩(wěn)定;而B+樹不論什么數(shù)據(jù)都需要查詢到葉子節(jié)點(diǎn)才可以找到,效率穩(wěn)定
  2. B+樹的非葉子節(jié)點(diǎn)不存放記錄,在 innoDB 中頁的默認(rèn)大小是 16KB,如果不存記錄就可以存儲(chǔ)更多的關(guān)鍵字,樹的高度就能進(jìn)一步降低,磁盤IO次數(shù)也得到降低。
  3. B+樹的葉子節(jié)點(diǎn)之間采用了指針連接,這樣一來就方便了順序遍歷,適合于范圍查詢

所以最終選用B+樹結(jié)構(gòu)。

Hash索引與B+樹索引的區(qū)別?

B+樹索引支持范圍查詢、模糊查詢,遵循最左匹配原則,而這些Hash索引都不支持,但是在等值查詢上Hash索引的效率要比B+樹效率高。

負(fù)載均衡算法

輪詢

? 把請(qǐng)求輪流發(fā)送到每個(gè)服務(wù)器上。

加權(quán)輪詢

? 在輪詢的基礎(chǔ)上,根據(jù)服務(wù)器的性能差異,為服務(wù)器賦予一定權(quán)值,性能高的服務(wù)器分配更高的權(quán)值。

輪詢的缺點(diǎn):每個(gè)請(qǐng)求的連接時(shí)間不一樣,使用輪詢可能會(huì)讓一臺(tái)服務(wù)器的當(dāng)前連接數(shù)過大,

最少鏈接

? 將請(qǐng)求發(fā)送給當(dāng)前連接數(shù)最少的服務(wù)器。

加權(quán)最少鏈接

? 在最少連接的基礎(chǔ)上,根據(jù)服務(wù)器的性能為每臺(tái)服務(wù)器分配權(quán)重,再根據(jù)權(quán)重計(jì)算出每臺(tái)服務(wù)器能處理的連接數(shù)。

加權(quán)隨機(jī)算法

? 根據(jù)服務(wù)器的配置和負(fù)載情況,配置不同的權(quán)重。然后按照權(quán)重來隨機(jī)選取服務(wù)器。

源地址哈希算法

? 對(duì)客戶端 IP 計(jì)算哈希值之后,再對(duì)服務(wù)器數(shù)量取模得到目標(biāo)服務(wù)器的序號(hào)??梢员WC相同的 IP 客戶端,如果服務(wù)器列表不變,將映射到同一個(gè)后臺(tái)服務(wù)器進(jìn)行訪問。

設(shè)計(jì)模式

設(shè)計(jì)模式了解嗎?

? 我理解的設(shè)計(jì)模式就是使用面向?qū)ο蟮氖址▽?shí)現(xiàn)可復(fù)用的代碼,在應(yīng)對(duì)需求發(fā)生變化時(shí)更易于擴(kuò)展,避免重復(fù)發(fā)明輪子,提高代碼復(fù)用性,使開發(fā)更簡潔高效。

用過哪些設(shè)計(jì)模式?

? 單例模式、

單例模式

? 保證一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)該實(shí)例的全局訪問點(diǎn),該實(shí)例被所有程序模塊共享。使用單例模式好處在于可以節(jié)省內(nèi)存資源。

其他

為什么選擇 C++ 后端

? 我覺得自己對(duì)業(yè)務(wù)這塊比較感興趣,客戶端的話對(duì)自己的審美不是很有自信,算法方向的話對(duì)學(xué)歷要求比較高,我自己是沒有讀研的打算,所以最后就選擇了后端方向。

? 為什么是C++后端的話,主要是覺得寫算法題 C++ 比較好用一點(diǎn),索性就選了 C++ 后端,還有一個(gè)原因是 java 后端太卷了,C++ 后端雖然崗位少一點(diǎn),但是可能沒有 java 后端那么卷。

一億個(gè)數(shù)據(jù)中選最大的1萬個(gè)

? 先拿出10000個(gè)建立小根堆,對(duì)于剩下的元素,如果大于堆頂元素的值,刪除堆頂元素,再進(jìn)行插入操作,否則直接跳過,這樣知道所有元素遍歷完,堆中的10000個(gè)就是最大的10000個(gè)。時(shí)間復(fù)雜度: m + (n-1)logm = O(nlogm)

一個(gè)5升瓶子一個(gè)3升瓶子,弄出4升水

  • 5升裝滿,倒入三升
  • 五升剩 2 升,三升倒掉
  • 五升全倒入三升,五升空,三升有 2 升
  • 五升裝滿,補(bǔ)滿三升,剩下的就是 4 升

各種排序算法的時(shí)間復(fù)雜度

  • 冒泡排序:最好O(n),最差O(n^2)
  • 選擇排序:最好O(n),最差O(n^2)
  • 插入排序:最好O(n),最差O(n^2)
  • 希爾排序:最好O(n),最差O(n^2)
  • 快速排序:平均是O(nlogn),最差O(n^2)
  • 歸并排序:恒為O(nlogn)

**快速排序的主要思想:**是分治和遞歸。首先從數(shù)組里任意選一個(gè)元素作為分界點(diǎn),然后根據(jù)該分界點(diǎn)把數(shù)組分成兩個(gè)區(qū)間,然后調(diào)整數(shù)組元素,使得左邊區(qū)間的所有元素都小于等于分界點(diǎn),右邊區(qū)間的所有元素都大于等于分界點(diǎn)。然后分別對(duì)左右兩個(gè)區(qū)間遞歸地進(jìn)行以上操作。最后就能得到有序數(shù)組。

**歸并排序的主要思想:**也是分治和遞歸。假設(shè)數(shù)組長度是 n。首先選取數(shù)組的中間元素作為分界點(diǎn)將數(shù)組劃分為兩個(gè)區(qū)間,然后對(duì)得到的兩個(gè)區(qū)間再進(jìn)行遞歸的劃分,最終會(huì)將整個(gè)數(shù)組劃分為n個(gè)區(qū)間,每個(gè)區(qū)間內(nèi)只有一個(gè)元素。然后,將成對(duì)的兩個(gè)區(qū)間按照順序有序地合并起來,就能得到有序的數(shù)組。

非遞歸快速排序的思路:非遞快排的核心是用棧模擬遞歸。初始時(shí)把整個(gè)區(qū)間的邊界入棧,然后取出來,對(duì)這段區(qū)間進(jìn)行單趟快排,接收返回值 index,index 將區(qū)間劃分為兩個(gè)子區(qū)間,然后把這兩個(gè)左右子區(qū)間分別入棧進(jìn)行快排。當(dāng)棧中元素為空時(shí)表示所有區(qū)間都已經(jīng)完成排序。文章來源地址http://www.zghlxwxcb.cn/news/detail-726351.html

到了這里,關(guān)于C++八股文的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 計(jì)算機(jī)復(fù)試面試基礎(chǔ)知識(shí)(八股文)(數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)、操作系統(tǒng)、計(jì)網(wǎng)、機(jī)組等)

    數(shù)據(jù)庫緒論 1、簡述三層模式、兩級(jí)映射,分別有什么作用? 模式(邏輯模式):是數(shù)據(jù)庫中全體數(shù)據(jù)的邏輯結(jié)構(gòu)和特征的描述,是數(shù)據(jù)庫系統(tǒng)模式結(jié)構(gòu)的中間層,即不涉及數(shù)據(jù)的物理存儲(chǔ)細(xì)節(jié),也與具體應(yīng)用程序開發(fā)工具語言無關(guān)。 外模式(用戶模式):是用戶能看見和使

    2023年04月09日
    瀏覽(73)
  • C++八股文

    C++八股文

    ? C++中,定義函數(shù) int function(int a[], int b),這里數(shù)組a會(huì)不會(huì)在內(nèi)存中拷貝(傳遞的是指針還是啥),什么情況下傳遞的是指針? ? 不會(huì),因?yàn)檫@里的 a 傳遞的是指針,和 int * 是一樣的。 優(yōu)先級(jí):() [] * 數(shù)組指針 ? 是一個(gè)指針,指向一個(gè)數(shù)組的起始地址。由于 [] 運(yùn)算符的優(yōu)

    2024年02月07日
    瀏覽(51)
  • C++面試八股文:技術(shù)勘誤

    C++面試八股文:技術(shù)勘誤

    不知不覺,《C++面試八股文》已經(jīng)更新30篇了,這是我第一次寫技術(shù)博客,由于個(gè)人能力有限,出現(xiàn)了不少紕漏,在此向各位讀者小伙伴們致歉。 為了不誤導(dǎo)更多的小伙伴,以后會(huì)不定期的出勘誤文章,請(qǐng)各位小伙伴留意。 在《C++面試八股文:C++中,設(shè)計(jì)一個(gè)類要注意哪些東

    2024年02月11日
    瀏覽(30)
  • 八股文C++篇(超級(jí)全)

    const int *a==int const *a:都是指a所指向的值不能改,但是a可以指向別的東西 const int a:a變量變成常量,不可修改 int *const a:a的值可以更改,但是指向它的指針不能更改 int const *const a:a本身和指向它的指針都不能更改 常函數(shù)內(nèi)不能修改成員變量 對(duì)于類的成員函數(shù),有時(shí)候必須指定

    2024年02月15日
    瀏覽(37)
  • C++面試八股文:如何避免死鎖?

    某日二師兄參加XXX科技公司的C++工程師開發(fā)崗位第31面: 面試官:什么是鎖?有什么作用? 二師兄:在C++中,鎖(Lock)是一種同步工具,用于保護(hù)共享資源,防止多個(gè)線程同時(shí)訪問,從而避免數(shù)據(jù)競爭和不一致。 面試官:有哪些鎖? 二師兄:從種類上分,可以分為普通鎖、

    2024年02月12日
    瀏覽(28)
  • C++面試八股文:什么是構(gòu)造函數(shù)?

    某日二師兄參加XXX科技公司的C++工程師開發(fā)崗位第29面: 面試官:什么是構(gòu)造函數(shù)? 二師兄:構(gòu)造函數(shù)是一種特殊的成員函數(shù),用于創(chuàng)建和初始化類的對(duì)象。構(gòu)造函數(shù)的名稱與類的名稱相同,并且沒有返回類型。構(gòu)造函數(shù)在對(duì)象被創(chuàng)建時(shí)自動(dòng)調(diào)用。 面試官:什么是默認(rèn)構(gòu)造

    2024年02月11日
    瀏覽(26)
  • C++面試八股文:什么是RAII?

    某日二師兄參加XXX科技公司的C++工程師開發(fā)崗位第13面: 面試官:什么是 RAII ? 二師兄: RAII 是 Resource Acquisition Is Initialization 的縮寫。翻譯成中文是資源獲取即初始化。 面試官: RAII 有什么特點(diǎn)和優(yōu)勢? 二師兄:主要的特點(diǎn)是,在對(duì)象初始化時(shí)獲取資源,在對(duì)象析構(gòu)時(shí)釋放

    2024年02月08日
    瀏覽(28)
  • C++面試八股文:聊一聊指針?

    C++面試八股文:聊一聊指針?

    某日二師兄參加XXX科技公司的C++工程師開發(fā)崗位第17面: 面試官:聊一聊指針? 二師兄:好的。 面試官:你覺得指針本質(zhì)上是什么? 二師兄:這要從內(nèi)存地址開始說起了。如果有一塊容量是1G的內(nèi)存,假設(shè)它的地址是從 0x00000000 到 0x3fffffff ,每一個(gè)字節(jié)都對(duì)應(yīng)一個(gè)地址。當(dāng)

    2024年02月09日
    瀏覽(24)
  • C++面試八股文:用過STL嗎?

    某日二師兄參加XXX科技公司的C++工程師開發(fā)崗位第21面: 面試官:用過STL嗎? 二師兄:(每天都用好嗎。。)用過一些。 面試官:你知道STL是什么? 二師兄:STL是指標(biāo)準(zhǔn)模板庫( Standard Template Library ),是C++區(qū)別于C語言的特征之一。 面試官:那你知道STL的六大部件是什么

    2024年02月09日
    瀏覽(15)
  • C++面試八股文:了解位運(yùn)算嗎?

    某日二師兄參加XXX科技公司的C++工程師開發(fā)崗位第12面: 面試官:了解位運(yùn)算嗎? 二師兄:了解一些。(我很熟悉) 面試官:請(qǐng)列舉以下有哪些位運(yùn)算? 二師兄:按位與( )、按位或( | )、按位異或( ^ ),按位取反( ~ )、左移( )和右移( )。 面試官:好的。那你

    2024年02月08日
    瀏覽(24)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包