oppo面經

用戶態與內核態的區別以及區分的緣由

http://www.javashuo.com/article/p-uhfawhjx-cm.htmlhtml

https://blog.csdn.net/qq_39823627/article/details/78736650程序員

  • 內核態與用戶態是操做系統的兩種運行級別,當程序運行在3級特權級上時,就能夠稱之爲運行在用戶態。由於這是最低特權級,是普通的用戶進程運行的特權級,大部分用戶直接面對的程序都是運行在用戶態;
  • 當程序運行在0級特權級上時,就能夠稱之爲運行在內核態。算法

  • 運行在用戶態下的程序不能直接訪問操做系統內核數據結構和程序。當咱們在系統中執行一個程序時,大部分時間是運行在用戶態下的,在其須要操做系統幫助完成某些它沒有權力和能力完成的工做時就會切換到內核態(好比操做硬件)。express

處於用戶態執行時,進程所能訪問的內存空間和對象受到限制,其所處於佔有的處理器是可被搶佔的數組

處於內核態執行時,則能訪問全部的內存空間和對象,且所佔有的處理器是不容許被搶佔的。緩存

定義和聲明的區別

從編譯原理上來講,聲明是僅僅告訴編譯器,有個某類型的變量會被使用,可是編譯器並不會爲它分配任何內存。而定義就是分配了內存。安全

int a = 0; //定義並聲明瞭變量 a extern int a; //只是聲明瞭有一個變量 a 存在,具體 a 在哪定義的,須要編譯器編譯的時候去找。

 如何判斷髮生內存泄漏

第一:良好的編碼習慣,儘可能在涉及內存的程序段,檢測出內存泄露。當程式穩定以後,在來檢測內存泄露時,無疑增長了排除的困難和複雜度。使用了內存分配的函數,一旦使用完畢,要記得要使用其相應的函數釋放掉。數據結構

第二:將分配的內存的指針以鏈表的形式自行管理,使用完畢以後從鏈表中刪除,程序結束時可檢查改鏈表。函數

第三:Boost 中的smart pointer。性能

爲何vector使用2倍擴容

聽說不少操做系統會使用夥伴系統(Buddy System)管理內存,因而乎用2^n的數組會不那麼容易形成內存碎片。更少的內存碎片 = 更少的內存整理時間。

爲何要有大端小端

大小端和CPU有關係,CISC(複雜指令集)CPU通常使用小端數據格式,RISC(精簡指令集)通常使用大端數據格式

判斷大小端

void judge_bigend_littleend3()
{
    union
    {
        int i;
        char c;
    }un;
    un.i = 1;

    if (un.c == 1)
        printf("小端\n");
    else
        printf("大端\n");
}

  

C++四種強制轉換

1) static_cast

用法:static_cast <類型說明符> (變量或表達式)

它主要有以下幾種用法:
    (1)用於類層次結構中基類和派生類之間指針或引用的轉換
      進行上行轉換(把派生類的指針或引用轉換成基類表示)是安全的
      進行下行轉換(把基類的指針或引用轉換爲派生類表示),因爲沒有動態類型檢查,因此是不安全的
    (2)用於基本數據類型之間的轉換,如把int轉換成char。這種轉換的安全也要開發人員來保證
    (3)把空指針轉換成目標類型的空指針
    (4)把任何類型的表達式轉換爲void類型
    注意:static_cast不能轉換掉expression的const、volitale或者__unaligned屬性。

static_cast:能夠實現C++中內置基本數據類型之間的相互轉換。

若是涉及到類的話,static_cast只能在有相互聯繫的類型中進行相互轉換,不必定包含虛函數

2) const_cast

在C語言中,const限定符一般被用來限定變量,用於表示該變量的值不能被修改。

而const_cast則正是用於強制去掉這種不能被修改的常數特性,但須要特別注意的是const_cast不是用於去除變量的常量性,而是去除指向常數對象的指針或引用的常量性,其去除常量性的對象必須爲指針或引用。

用法:const_cast<type_id> (expression)
    該運算符用來修改類型的const或volatile屬性。除了const 或volatile修飾以外, type_id和expression的類型是同樣的。
    常量指針被轉化成很是量指針,而且仍然指向原來的對象;
    常量引用被轉換成很是量引用,而且仍然指向原來的對象;常量對象被轉換成很是量對象。

常量指針——指向「常量」的指針(const int *p, int const *p)

指針常量——指針類型的常量(int *const p)

3) reinterpret_cast

在C++語言中,reinterpret_cast主要有三種強制轉換用途:改變指針或引用的類型將指針或引用轉換爲一個足夠長度的整形將整型轉換爲指針或引用類型

用法:reinterpret_cast<type_id> (expression)
    type-id必須是一個指針、引用、算術類型、函數指針或者成員指針
    它能夠把一個指針轉換成一個整數,也能夠把一個整數轉換成一個指針(先把一個指針轉換成一個整數,在把該整數轉換成原類型的指針,還能夠獲得原先的指針值)。
    在使用reinterpret_cast強制轉換過程僅僅只是比特位的拷貝,所以在使用過程當中須要特別謹慎

4) dynamic_cast

 用法:dynamic_cast<type_id> (expression)

 (1)其餘三種都是編譯時完成的,dynamic_cast是運行時處理的運行時要進行類型檢查

(2)不能用於內置的基本數據類型的強制轉換

(3)dynamic_cast轉換若是成功的話返回的是指向類的指針或引用轉換失敗的話則會返回NULL

(4)使用dynamic_cast進行轉換的,基類中必定要有虛函數,不然編譯不經過

        B中須要檢測有虛函數的緣由:類中存在虛函數,就說明它有想要讓基類指針或引用指向派生類對象的狀況,此時轉換纔有意義。

(5)在類的轉換時,在類層次間進行上行轉換時,dynamic_cast和static_cast的效果是同樣的。在進行下行轉換時,dynamic_cast具備類型檢查的功能,比static_cast更安全。

        向上轉換,即爲子類指針指向父類指針(通常不會出問題);向下轉換,即將父類指針轉化子類指針。

       向下轉換的成功與否還與將要轉換的類型有關,即要轉換的指針指向的對象的實際類型與轉換之後的對象類型必定要相同,不然轉換失敗。

        在C++中,編譯期的類型轉換有可能會在運行時出現錯誤,特別是涉及到類對象的指針或引用操做時,更容易產生錯誤。Dynamic_cast操做符則能夠在運行期對可能產生問題的類型轉換進行測試。

函數調用時棧空間的變化:

https://blog.csdn.net/xi_niuniu/article/details/44978207#commentBox

函數調用大體包括如下幾個步驟:

參數入棧:將參數從右向左依次壓入系統棧中
返回地址入棧:將當前代碼區調用指令的下一條指令地址壓入棧中,供函數返回時繼續執行
代碼區跳轉:處理器從當前代碼區跳轉到被調用函數的入口處
棧幀調整:具體包括
保存當前棧幀狀態值,已備後面恢復本棧幀時使用(EBP入棧)
將當前棧幀切換到新棧幀。(將ESP值裝入EBP,更新棧幀底部
給新棧幀分配空間。(把ESP減去所需空間的大小,擡高棧頂

 堆和棧的區別:

管理方式:對於棧來說,是由編譯器自動管理,無需手動控制;對於堆來講,分配和釋放都是由程序員控制的

空間大小:整體來講,棧的空間是要小於堆的。通常來說在32位系統下,堆內存能夠達到4G的空間,從這個角度來看堆內存幾乎是沒有什麼限制的;可是對於棧來說,通常是有必定的空間大小的

碎片問題:對於堆來說,因爲分配和釋放是由程序眼控制的(利用new/delete 或 malloc/free),頻繁的操做勢必會形成內存空間的不連續,從而形成大量的內存碎片,使程序效率下降。對於棧來說,則不會存在這個問題,由於棧是先進後出的數據結構,在某一對象彈出以前,它以前的全部對象都已經彈出。

生長方向:對於堆來說,生長方向是向上的,也就是沿着內存地址增長的方向,對於棧來說,它的生長方式是向下的,也就是沿着內存地址減少的方向增加。

分配方式:堆都是動態分配的,沒有靜態分配的堆。棧有兩種分配方式:靜態分配和動態分配,靜態分配是編譯器完成的,好比局部變量的分配;動態分配由alloca函數進行分配,可是棧的動態分配和堆是不一樣的,它的動態分配是由編譯器實現的,無需咱們手工實現。

分配效率:棧是機器系統提供的數據結構,計算機會在底層對棧提供支持,分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率很高。堆則是C/C++函數提供的,它的機制是很複雜的,例如爲了分配一塊內存,庫函數會按照必定的算法在堆內存中搜索可用的足夠大小的空間,若是沒有足夠大小的空間(多是因爲碎片太多),就有可能調用系統功能去增長程序數據段的內存空間,這樣就有機會分到足夠大小的內存,而後進行返回。顯然,堆的效率要比棧底的多。

請說一下C/C++ 中指針和引用的區別?

1.指針有本身的一塊空間,而引用只是一個別名;

2.使用sizeof看一個指針的大小是4,而引用則是被引用對象的大小;

3.指針能夠被初始化爲NULL,而引用必須被初始化且必須是一個已有對象 的引用;

4.做爲參數傳遞時,指針須要被解引用才能夠對對象進行操做,而直接對引 用的修改都會改變引用所指向的對象;

5.能夠有const指針,可是沒有const引用;

6.指針在使用中能夠指向其它對象,可是引用只能是一個對象的引用,不能 被改變;

7.指針能夠有多級指針(**p),而引用止於一級;

8.指針和引用使用++運算符的意義不同;

9.若是返回動態內存分配的對象或者內存,必須使用指針,引用可能引發內存泄露。

TCP/IP中如何解決粘包問題?若是一直傳輸數據怎麼拆包?

粘包、拆包發生緣由
發生TCP粘包或拆包有不少緣由,現列出常見的幾點,可能不全面,歡迎補充,
一、要發送的數據大於TCP發送緩衝區剩餘空間大小,將會發生拆包
二、待發送數據大於MSS(最大報文長度),TCP在傳輸前將進行拆包
三、要發送的數據小於TCP發送緩衝區的大小,TCP將屢次寫入緩衝區的數據一次發送出去,將會發生粘包
四、接收數據端的應用層沒有及時讀取接收緩衝區中的數據,將發生粘包

粘包、拆包解決辦法
經過以上分析,咱們清楚了粘包或拆包發生的緣由,那麼如何解決這個問題呢?解決問題的關鍵在於如何給每一個數據包添加邊界信息,經常使用的方法有以下幾個:
一、發送端給每一個數據包添加包首部,首部中應該至少包含數據包的長度,這樣接收端在接收到數據後,經過讀取包首部的長度字段,便知道每個數據包的實際長度了。
二、發送端將每一個數據包封裝爲固定長度(不夠的能夠經過補0填充),這樣接收端每次從接收緩衝區中讀取固定長度的數據就天然而然的把每一個數據包拆分開來。
三、能夠在數據包之間設置邊界,如添加特殊符號,這樣,接收端經過這個邊界就能夠將不一樣的數據包拆分開。

TCP報文頭部多長?整個報文最長多長?

TCP數據包大小 1500 - IP頭(20B)- TCP頭(20B) = 1460B 

Cache和Buffer的區別

1. Cache:緩存區,是高速緩存,是位於CPU和主內存之間的容量較小但速度很快的存儲器,由於CPU的速度遠遠高於主內存的速度,CPU從內存中讀取數據需等待很長的時間,而  Cache保存着CPU剛用過的數據或循環使用的部分數據,這時從Cache中讀取數據會更快,減小了CPU等待的時間,提升了系統的性能。

    Cache並非緩存文件的,而是緩存塊的(塊是I/O讀寫最小的單元);Cache通常會用在I/O請求上,若是多個進程要訪問某個文件,能夠把此文件讀入Cache中,這樣下一個進程獲取CPU控制權並訪問此文件直接從Cache讀取,提升系統性能。

2. Buffer:緩衝區,用於存儲速度不一樣步的設備或優先級不一樣的設備之間傳輸數據;經過buffer能夠減小進程間通訊須要等待的時間,當存儲速度快的設備與存儲速度慢的設備進行通訊時,存儲慢的數據先把數據存放到buffer,達到必定程度存儲快的設備再讀取buffer的數據,在此期間存儲快的設備CPU能夠幹其餘的事情。

Buffer:通常是用在寫入磁盤的,例如:某個進程要求多個字段被讀入,當全部要求的字段被讀入以前已經讀入的字段會先放到buffer中。

構造順序

基類構造函數,派生類對象成員構造函數,派生類自己的構造函數

析構順序

派生類自己的析構函數、對象成員析構函數、基類析構函數

reserve,resize,size,capacity:

resize()

既分配了空間,也建立了對象。這裏空間就是capacity,對象就是容器中的元素

reserve()

reserve()表示容器預留空間,但不是真正的建立對象,須要經過insert()或push_back()等操做建立對象。push_back是在finshish處construct

相關文章
相關標籤/搜索