1. 程式人生 > >2018.09面試題總結

2018.09面試題總結

我在2018年9月份參加了幾家公司的筆試,也在牛客網上做了一些題目,下面我把稍微做個小結。

1 問題:為什麼不能返回區域性變數地址,卻可以返回區域性變數? 區域性變數用作返回值時,會生成一個區域性變數的拷貝用作返回值,之後區域性變數會被系統回收; 函式不能返回區域性變數的地址,因為如果返回區域性變數的地址,系統回收後,指標、引用指向的內容就無意義了。

2 傳地址或者引用時,形參與實參都有相同的地址; 傳值呼叫時,只是將資料進行了複製,不會改變實參的值。

3 面向物件的五大基本原則

單一職責原則(Single-ResposibilityPrinciple): 一個類,最好只做一件事,只有一個引起它的變化。單一職責原則可以看做是低耦合、高內聚在面向物件原則上的引申, 將職責定義為引起變化的原因,以提高內聚性來減少引起變化的原因。

開放封閉原則(Open-Closed principle): 軟體實體應該是可擴充套件的,而不可修改的。也就是,對擴充套件開放,對修改封閉的。

裡式替換原則(Liskov-SubstituionPrinciple): 子類必須能夠替換其基類。這一思想體現為對繼承機制的約束規範,只有子類能夠替換基類時,才能保證系統在執行期內識別子類,這是保證繼承複用的基礎。

依賴倒置原則(Dependecy-InversionPrinciple): 依賴於抽象。具體而言就是高層模組不依賴於底層模組,二者都同依賴於抽象;抽象不依賴於具體,具體依賴於抽象。

介面隔離原則(Interface-Segregation Principle): 使用多個小的專門的介面,而不要使用一個大的總介面

4 編譯器只要能完成程式語言翻譯成機器語言即可,所以任意的語言均可實現。

5 巨集與型別無關,但是C++中的函式必須指定返回型別,故巨集可以做函式不能做的事。   巨集只是預定義的函式,在編譯階段不進行型別安全性檢查,在編譯的時候將對應函式用巨集命令替換。對程式效能無影響。   6 對於堆來說,生長方向是向上的,也就是向著地址增加的方向;對於棧來說,它的生長方向是向下的,就是向著記憶體地址減小的方向生長。   在題   union X   {     unint16_t a;     struct Z     {         unint8_t m;         unint8_t n;     }z;   };   union X x;   x.a = 0x1234;     union 就是公用一塊記憶體,在這題中,a記憶體與n和m的是在一起,而這是在棧中,棧中的記憶體分配是大地址向小地址分配的,   所以&m > &n,而大位元組序就是一個字的高位元組對應高地址,低位元組對應低地址,所以m中應該是0x1234中的高位元組部分12,   而n中對應的就是低位元組部分34;小位元組序正好與這個相反,m對應34,n對應12。   7 使用者態切換到核心態的 3 種方式 a.  系統呼叫 b.  異常 c.  外圍裝置的中斷

8 編譯的完整過程: C源程式-->預編譯處理(.c)-->編譯、優化程式(.s、.asm)-->彙編程式(.obj、.o、.a、.ko)-->連結程式(.exe、.elf、.axf等)

9 指向虛擬函式表的指標在32位系統下佔用4個位元組,其地址分佈在整個類成員變數的地址的首部,接下來就是變數a的地址、b的地址。當將test物件obj賦給指向整型的pInt後,指標pInt指向了地址的首部也就是虛擬函式表指標,所以*(pInt+0)=100改變的是虛擬函式表的值,接下來*(pInt+1)=200改變的是變數a的值,變數b沒有變換。

10 抽象類 : 類中至少有一個方法是抽象方法,則該類就是抽象類 介面 :類中的方法全部都是抽象方法。

11 建構函式特點:   a)沒有函式返回值型別   b)必須與本類名完全相同 c)當沒有為一個類顯示的定義一個建構函式時,系統將自動分配一個預設的無參的方法體為空的建構函式。如果定義了一個建構函式,那麼預設的就沒有了。

12 初始化為NULL(0)的類指標可以安全的呼叫不涉及類成員變數的類成員函式而不出錯,但是如果類成員函式中呼叫了類成員變數則會出錯,原因是呼叫成員函式的時候,函式地址是編譯期間確定的,成員函式不通過物件指標去呼叫,物件指標僅僅作為引數傳入函式然後去呼叫成員變數。而如果是虛擬函式,需要通過this去計算vptr,然後找到vtable,而this為NULL,因此會報錯。

13 連結:https://www.nowcoder.com/questionTerminal/09a55229fc004c2ba921678246f8d7f8 來源:牛客網

st_task: id        value     timestamp 0000|0000|0000 0000|0000 0000 0000 0000 (physical)   a: 0000 0000 0001 0001   (logic) 0001 0001 0000 0000   (physical)   st_task: id        value     timestamp 0001|0001|0000 0000|0000 0000 0000 0000 1        |0        |0

14 reinterpret_cast:一個指標轉化為其他型別的指標時,不做型別檢測,操作結果是一個指標指向另一個指標的值的二進位制拷貝;

static_cast:允許執行隱式轉換和相反的轉換操作,父類轉換為子類是強制轉換Son *son=static_cast(father),而子類轉換為父類就是隱式轉換;

dynamic_cast:用於物件的指標和引用,當用於多型型別轉換時,允許隱式轉換及相反的過程中,與static_cast的不同之處在於,在相反的轉換過程中,dynamic_cast會檢測操作的有效性,如果返回的不是被 請求的 有效完整物件,則返回null,反之返回這個有效的物件,如果是引用返回無效時則會丟擲bad_cast異常;

const_cast:這個轉換操作會操縱傳遞物件的const屬性,或者設定或者移除該屬性。

15 ++ 是一目運算子,自增運算,它只能用於一個變數,即變數值自增1, 不能用於表示式。 ++(a++) 裡,小括號優先。 (a++) 是 表示式,按運算規則,不能對 表示式 作 自增運算.

16 函式模板呼叫時不需要顯式指定型別,系統自動匹配引數型別,若沒有合適的,會進行報錯。而類模板使用需要顯式指定型別。

17 Nagle演算法的規則: (1)如果包長度達到MSS,則允許傳送; (2)如果該包含有FIN,則允許傳送; (3)設定了TCP_NODELAY選項,則允許傳送; (4)未設定TCP_CORK選項時,若所有發出去的小資料包(包長度小於MSS)均被確認,則允許傳送; (5)上述條件都未滿足,但發生了超時(一般為200ms),則立即傳送。

Nagle演算法只允許一個未被ACK的包存在於網路,它並不管包的大小,因此它事實上就是一個擴充套件的停-等協議,只不過它是基於包停-等的,而不是基於位元組停-等的。Nagle演算法完全由TCP協議的ACK機制決定,這會帶來一些問題,比如如果對端ACK回覆很快的話,Nagle事實上不會拼接太多的資料包,雖然避免了網路擁塞,網路總體的利用率依然很低。 Nagle演算法是silly window syndrome(SWS)預防演算法的一個半集。SWS演算法預防傳送少量的資料,Nagle演算法是其在傳送方的實現,而接收方要做的是不要通告緩衝空間的很小增長,不通知小視窗,除非緩衝區空間有顯著的增長。這裡顯著的增長定義為完全大小的段(MSS)或增長到大於最大視窗的一半。 注意:BSD的實現是允許在空閒連結上傳送大的寫操作剩下的最後的小段,也就是說,當超過1個MSS資料傳送時,核心先依次傳送完n個MSS的資料包,然後再發送尾部的小資料包,其間不再延時等待。(假設網路不阻塞且接收視窗足夠大) 舉個例子,比如之前的blog中的實驗,一開始client端呼叫socket的write操作將一個int型資料(稱為A塊)寫入到網路中,由於此時連線是空閒的(也就是說還沒有未被確認的小段),因此這個int型資料會被馬上傳送到server端,接著,client端又呼叫write操作寫入‘\r\n’(簡稱B塊),這個時候,A塊的ACK沒有返回,所以可以認為已經存在了一個未被確認的小段,所以B塊沒有立即被髮送,一直等待A塊的ACK收到(大概40ms之後),B塊才被髮送。整個過程如圖所示: 這裡還隱藏了一個問題,就是A塊資料的ACK為什麼40ms之後才收到?這是因為TCP/IP中不僅僅有nagle演算法,還有一個TCP確認延遲機制 。當Server端收到資料之後,它並不會馬上向client端傳送ACK,而是會將ACK的傳送延遲一段時間(假設為t),它希望在t時間內server端會向client端傳送應答資料,這樣ACK就能夠和應答資料一起傳送,就像是應答資料捎帶著ACK過去。在我之前的時間中,t大概就是40ms。這就解釋了為什麼'\r\n'(B塊)總是在A塊之後40ms才發出。 當然,TCP確認延遲40ms並不是一直不變的,TCP連線的延遲確認時間一般初始化為最小值40ms,隨後根據連線的重傳超時時間(RTO)、上次收到資料包與本次接收資料包的時間間隔等引數進行不斷調整。另外可以通過設定TCP_QUICKACK選項來取消確認延遲。

18 同步是害怕在操作過程的時候被其他執行緒也進行讀取操作,一旦是原子性的操作就不會發生這種情況。 因為一步到位的操作,其他執行緒不可能在中間干涉。另外三項都有讀取、操作兩個步驟,而X=1則是原子性操作。

19 int&)a:將a的引用強制轉換為整型,意思是a所在的記憶體,本來定義的時候為float型別並初始為1.0f,但現在我要按int型別解釋這段記憶體(也就是說a所在的記憶體地址中的資料本來是按float型儲存表示的,你非要按int型來解釋不可)。 1.0f   在記憶體中的儲存為 0   011   1111   1   000   0000   0000   0000   0000   0000. 把他按整型數解釋為2^29+2^28+2^27+2^26+2^25+2^24+2^23=1065353216 (int&)a 相當於*(int*)&a ,*(int*)(&a),*((int*)&a)   (int)a   a在記憶體中的值轉換成int型別   

20 非常量引用的初始值必須為左值   要理解這個先得理解左值和右值的概念 一個區分左值與右值的便捷方法是:看能不能對錶達式取地址,如果能,則為左值,否則為右值。

本題舉例: 執行f1(0),實參0要傳成A物件,那麼執行 A &a1 = 0;   //這是不行的。 執行f2(0),實參0要傳成A物件,那麼執行 const A &a2 = 0;//這是可行的。

21 檔案指標指向的是一塊記憶體區域,這塊區域儲存著開啟的檔案的相關資訊, 包括檔案讀取指標當前位置、檔案讀取緩衝區大小等資訊,並不是指向檔案的。 fscanf是從檔案中格式化讀取,fprintf才是向檔案中格式化寫入。

22 當基類建構函式需要外部傳遞引數才能進行初始化時,派生類必須顯式定義建構函式, 為基類傳遞引數;基類如果不需要傳遞或者可以不傳遞引數,派生類可以不用顯式定義建構函式。

23 使用 inline 關鍵字的函式只是使用者希望它成為行內函數,但編譯器有權忽略這個請求,比如:若此函式體太大,則不會把它作為行內函數展開的。 標頭檔案中不僅要包含 inline 函式的宣告,而且必須包含定義,且在定義時必須加上 inline 。【關鍵字 inline 必須與函式定義體放在一起才能使函式成為內聯,僅將 inline 放在函式宣告前面不起任何作用】 inline 函式可以定義在原始檔中,但多個原始檔中的同名 inline 函式的實現必須相同。一般把 inline 函式的定義放在標頭檔案中更加合適。 類內的成員函式,預設都是 inline 的。【定義在類宣告之中的成員函式將自動地成為行內函數】 不管是 class 宣告中定義的 inline 函式,還是 class 實現中定義的 inline 函式,不存在優先不優先的問題,因為 class 的成員函式都是 inline 的,加了關鍵字 inline 也沒什麼特殊的。

24 陣列作為函式的引數會退化成指標

25  含有純虛擬函式的類是抽象類,對於繼承這樣的類的類(派生類)來說,如果派生類實現了基類的純虛擬函式,則派生類可以例項化。 若派生類沒有實現該純虛擬函式,則該派生類也是抽象類,即不能例項化。 綜上所述,派生類不一定非得要實現基類的純虛擬函式。

26  A:“很多程式設計師希望STL實現是完全執行緒安全的“。所以不安全。 B:vector的存在可以使開發者不必關心記憶體的申請和釋放。但是,vector的一個缺點就是它的記憶體分配是按照2的倍數分配記憶體的。 C:錯誤。要知道 std::sort 不是穩定的排序演算法,它不保證“相等”元素的相對位置,使用 std::stable_sort 來保證這一點 D:STL的容器可以分為以下幾個大類: 一:序列容器, 有vector, list, deque, string. 二 : 關聯容器,     有set, multiset, map, mulmap, hash_set, hash_map, hash_multiset, hash_multimap 三: 其他的雜項: stack, queue, valarray, bitset E:正確。堆疊是一個線性表,插入刪除操作都在一端進行,deque是先進先出的,操作原理和stack是一樣的

27 函式的引數是從右向左壓棧的,輸出時從棧頂開始,相當於: int i = 3;  ++i; ++i; printf("%d,%d",i,i);所以是 5,5; 再舉一個例子,int i = 1; printf("%d,%d", i += 2, i *= 3); 在輸出i之前先進行了i *= 3和 i += 2;最終i = 5;所以結果是5,5;

28 作業系統中引入多道處理技術是為了提高CPU和I/O裝置的(利用率)。

29 系統中的“顛簸”是由(缺頁率高)引起的

30 使用SPOOLING技術實現(虛擬裝置)。 spooling(外部裝置聯機並行操作)技術實現了虛擬裝置功能。多個程序同時使用一獨享裝置,而對每一個程序而言,都認為自己獨佔這一裝置。 裝置並沒有分配給任何程序,在輸入井或輸出井中,分配給程序的是一儲存區和建立一張io請求表。

31 站點A、B、C通過CDMA共享鏈路,A、B、C的碼片序列(chipping  sequence)分別是(1,1,1,1)、(1,-1,1,-1)和(1,1,-1,-1),若C從鏈路上收到的序列是(2,0,2,0,0,-2,0,-2,0,2,0,2),則C收到A傳送的資料是 (101 )

C接收到的是A,B傳送過來的疊加碼片,C想要看A傳送的資料,就將接收到的疊加碼片與A的碼片序列進行規格化內積操作:(2,0,2,0;0,-2,0,-2;0,2,0,2)每四位與(1,1,1,1)進行規格化內積,(2*1+0*1+2*1+0*1)/4=1;(0*1+-2*1+0*1+-2*1)/4=-1,-1即0;(0*1+2*1+0*1+2*1)/4=1;可以得到結果101

32 路由器的缺點是(成為網路瓶頸)

33  HTTP 2.0新特性 增加二進位制分幀 壓縮頭部(header compression) 多路複用(multiplexing) 請求優先順序 伺服器提示(server push)

34 頁表的作用是實現從頁號到物理塊號的(地址對映) 在頁式管理中,頁表的作用是實現從頁號到物理塊號的地址對映,儲存頁表的作用是記錄記憶體頁面的分配情況。 頁表實際上就是程序的虛存空間與系統中的物理儲存空間的一個對映關係。因為每個程序都有自己獨立的虛存空間,所以作業系統需要為每個程序儲存一個頁表。程序切換的時候作業系統就會把即將排程執行的那個程序的頁表載入MMU,完成地址空間的切換。

35 如果要持續的 ping 某個地址,可以使用下列哪個引數?(    )  -t :不停的ping對方主機,直到你按下Control-C; -a:解析計算機NetBios名; -n:傳送count指定的Echo資料包數; -l :定義echo資料包大小。