1、編程語言html
1.根據熟悉的語言,談談兩種語言的區別?java
主要淺談下C/C++和PHP語言的區別:node
1)PHP弱類型語言,一種腳本語言,對數據的類型不要求過多,較多的應用於Web應用開發,如今好多互聯網開發公司的主流web後臺開發語言,主要框架爲mvc模型,如smarty,yaf,升級的PHP7速度較快,對服務器的壓力要小不少,在新浪微博已經有應用,對比很明顯。react
2)C/C++開發語言,C語言更偏向硬件底層開發,C++語言是目前爲止我認爲語法內容最多的一種語言。C/C++在執行速度上要快不少,畢竟其餘類型的語言大都是C開發的,更多應用於網絡編程和嵌入式編程。linux
2.volatile是幹啥用的,(必須將cpu的寄存器緩存機制回答的很透徹),使用實例有哪些?(重點)ios
1)訪問寄存器比訪問內存單元要快,編譯器會優化減小內存的讀取,可能會讀髒數據。聲明變量爲volatile,編譯器再也不對訪問該變量的代碼優化,仍然從內存讀取,使訪問穩定。c++
總結:volatile關鍵詞影響編譯器編譯的結果,用volatile聲明的變量表示該變量隨時可能發生變化,與該變量有關的運算,再也不編譯優化,以避免出錯。程序員
2)使用實例以下(區分C程序員和嵌入式系統程序員的最基本的問題。):web
並行設備的硬件寄存器(如:狀態寄存器)
一箇中斷服務子程序中會訪問到的非自動變量(Non-automatic variables)
多線程應用中被幾個任務共享的變量
3)一個參數既能夠是const還能夠是volatile嗎?解釋爲何。面試
能夠。一個例子是隻讀的狀態寄存器。它是volatile由於它可能被意想不到地改變。它是const由於程序不該該試圖去修改它。
4)一個指針能夠是volatile 嗎?解釋爲何。
能夠。儘管這並不很常見。一個例子當中斷服務子程序修該一個指向一個buffer的指針時。
下面的函數有什麼錯誤:
int square(volatile int *ptr) {
return *ptr * *ptr;
}
下面是答案:
這段代碼有點變態。這段代碼的目的是用來返指針*ptr指向值的平方,可是,因爲*ptr指向一個volatile型參數,編譯器將產生相似下面的代碼:
int square(volatile int *ptr){
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
因爲*ptr的值可能被意想不到地該變,所以a和b多是不一樣的。結果,這段代碼可能返不是你所指望的平方值!正確的代碼以下:
long square(volatile int *ptr){
int a;
a = *ptr;
return a * a;
}
3.static const等等的用法,(能說出越多越好)(重點)
² 首先說說const的用法(絕對不能說是常數)
1)在定義的時候必須進行初始化
2)指針能夠是const 指針,也能夠是指向const對象的指針
3)定義爲const的形參,即在函數內部是不能被修改的
4)類的成員函數能夠被聲明爲常成員函數,不能修改類的成員變量
5)類的成員函數能夠返回的是常對象,即被const聲明的對象
6)類的成員變量是常成員變量不能在聲明時初始化,必須在構造函數的列表裏進行初始化
(注:千萬不要說const是個常數,會被認爲是外行人的!!!!哪怕說個只讀也行)
下面的聲明都是什麼意思?
const int a; a是一個常整型數
int const a; a是一個常整型數
const int *a; a是一個指向常整型數的指針,整型數是不可修改的,但指針能夠
int * const a; a爲指向整型數的常指針,指針指向的整型數能夠修改,但指針是不可修改的
int const * a const; a是一個指向常整型數的常指針,指針指向的整型數是不可修改的,同時指針也是不可修改的
經過給優化器一些附加的信息,使用關鍵字const也許能產生更緊湊的代碼。合理地使用關鍵字const能夠使編譯器很天然地保護那些不但願被改變的參數,防止其被無心的代碼修改。簡而言之,這樣能夠減小bug的出現。
Const如何作到只讀?
這些在編譯期間完成,對於內置類型,如int, 編譯器可能使用常數直接替換掉對此變量的引用。而對於結構體不必定。
² 再說說static的用法(三個明顯的做用必定要答出來)
1)在函數體,一個被聲明爲靜態的變量在這一函數被調用過程當中維持其值不變。
2)在模塊內(但在函數體外),一個被聲明爲靜態的變量能夠被模塊內所用函數訪問,但不能被模塊外其它函數訪問。它是一個本地的全局變量。
3)在模塊內,一個被聲明爲靜態的函數只可被這一模塊內的其它函數調用。那就是,這個函數被限制在聲明它的模塊的本地範圍內使用
4)類內的static成員變量屬於整個類所擁有,不能在類內進行定義,只能在類的做用域內進行定義
5)類內的static成員函數屬於整個類所擁有,不能包含this指針,只能調用static成員函數
static全局變量與普通的全局變量有什麼區別?static局部變量和普通局部變量有什麼區別?static函數與普通函數有什麼區別?
static全局變量與普通的全局變量有什麼區別:static全局變量只初使化一次,防止在其餘文件單元中被引用;
static局部變量和普通局部變量有什麼區別:static局部變量只被初始化一次,下一次依據上一次結果值;
static函數與普通函數有什麼區別:static函數在內存中只有一份,普通函數在每一個被調用中維持一份拷貝
4.extern c 做用
告訴編譯器該段代碼以C語言進行編譯。
5.指針和引用的區別
1)引用是直接訪問,指針是間接訪問。
2)引用是變量的別名,自己不單獨分配本身的內存空間,而指針有本身的內存空間
3)引用綁定內存空間(必須賦初值),是一個變量別名不能更改綁定,能夠改變對象的值。
總的來講:引用既具備指針的效率,又具備變量使用的方便性和直觀性
6. 關於靜態內存分配和動態內存分配的區別及過程
1) 靜態內存分配是在編譯時完成的,不佔用CPU資源;動態分配內存運行時完成,分配與釋放須要佔用CPU資源;
2)靜態內存分配是在棧上分配的,動態內存是堆上分配的;
3)動態內存分配須要指針或引用數據類型的支持,而靜態內存分配不須要;
4)靜態內存分配是按計劃分配,在編譯前肯定內存塊的大小,動態內存分配運行時按需分配。
5)靜態分配內存是把內存的控制權交給了編譯器,動態內存把內存的控制權交給了程序員;
6)靜態分配內存的運行效率要比動態分配內存的效率要高,由於動態內存分配與釋放須要額外的開銷;動態內存管理水平嚴重依賴於程序員的水平,處理不當容易形成內存泄漏。
7. 頭文件中的 ifndef/define/endif 幹什麼用?
預處理,防止頭文件被重複使用,包括pragma once都是這樣的
8. 宏定義求兩個元素的最小值
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
9. 分別設置和清除一個整數的第三位?
#define BIT3 (0x1<<3)
static int a;
void set_bit3(void){
a |= BIT3;
}
void clear_bit3(void){
a &= ~BIT3;
}
10. 用預處理指令#define 聲明一個常數,用以代表1年中有多少秒
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
11. 預處理器標識#error的目的是什麼?
拋出錯誤提示,標識外部宏是否被定義!
12. 嵌入式系統中常常要用到無限循環,你怎麼樣用C編寫死循環呢?
記住這是第一方案!!!!
while(1)
{
}
一些程序員更喜歡以下方案:
for(;;){
}
彙編語言的無線循環是:
Loop:
...
goto Loop;
13. 用變量a給出下面的定義
一個有10個指針的數組,該指針指向一個函數,該函數有一個整型參數並返回一個整型數 int (*a[10])(int);
14. 中斷是嵌入式系統中重要的組成部分,這致使了不少編譯開發商提供一種擴展—讓標準C支持中斷。具表明事實是,產生了一個新的關鍵字 __interrupt
16. memcpy函數的實現
void *memcpy(void *dest, const void *src, size_t count) {
char *tmp = dest;
const char *s = src;
while (count--)
*tmp++ = *s++;
return dest;
}
17. Strcpy函數實現
char *strcpy(char *dst,const char *src) {
assert(dst != NULL && src != NULL);
char *ret = dst;
while((* dst++ = * src++) != '\0') ;
return ret;
}
18. strcat函數的實現
char *strcat(char *strDes, const char *strSrc){
assert((strDes != NULL) && (strSrc != NULL));
char *address = strDes;
while (*strDes != ‘\0′)
++ strDes;
while ((*strDes ++ = *strSrc ++) != ‘\0′)
return address;
}
19.strncat實現
char *strncat(char *strDes, const char *strSrc, int count){
assert((strDes != NULL) && (strSrc != NULL));
char *address = strDes;
while (*strDes != ‘\0′)
++ strDes;
while (count — && *strSrc != ‘\0′ )
*strDes ++ = *strSrc ++;
*strDes = ‘\0′;
return address;
}
20. strcmp函數實現
int strcmp(const char *str1,const char *str2){
/*不可用while(*str1++==*str2++)來比較,當不相等時仍會執行一次++,
return返回的比較值其實是下一個字符。應將++放到循環體中進行。*/
while(*str1 == *str2){
if(*str1 == '\0')
return0;
++str1;
++str2;
}
return *str1 - *str2;
}
21. strncmp實現
int strncmp(const char *s, const char *t, int count){
assert((s != NULL) && (t != NULL));
while (*s && *t && *s == *t && count –) {
++ s;
++ t;
}
return (*s – *t);
}
22.strlen函數實現
int strlen(const char *str){
assert(str != NULL);
int len = 0;
while (*str ++ != ‘\0′)
++ len;
return len;
}
23. strpbrk函數實現
char * strpbrk(const char * cs,const char * ct){
const char *sc1,*sc2;
for( sc1 = cs; *sc1 != '\0'; ++sc1){
for( sc2 = ct; *sc2 != '\0'; ++sc2){
if (*sc1 == *sc2){
return (char *) sc1;
}
}
}
return NULL;
}
24. strstr函數實現
char *strstr(const char *s1,const char *s2){
int len2;
if(!(len2=strlen(s2)))//此種狀況下s2不能指向空,不然strlen沒法測出長度,這條語句錯誤
return(char*)s1;
for(;*s1;++s1)
{
if(*s1==*s2 && strncmp(s1,s2,len2)==0)
return(char*)s1;
}
return NULL;
}
25. string實現(注意:賦值構造,operator=是關鍵)
class String{
public:
//普通構造函數
String(const char *str = NULL);
//拷貝構造函數
String(const String &other);
//賦值函數
String & operator=(String &other) ;
//析構函數
~String(void);
private:
char* m_str;
};
分別實現以上四個函數
//普通構造函數
String::String(const char* str){
if(str==NULL) //若是str爲NULL,存空字符串{
m_str = new char[1]; //分配一個字節
*m_str = ‘\0′; //賦一個’\0′
}else{
str = new char[strlen(str) + 1];//分配空間容納str內容
strcpy(m_str, str); //複製str到私有成員m_str中
}
}
//析構函數
String::~String(){
if(m_str!=NULL) //若是m_str不爲NULL,釋放堆內存{
delete [] m_str;
m_str = NULL;
}
}
//拷貝構造函數
String::String(const String &other){
m_str = new char[strlen(other.m_str)+1]; //分配空間容納str內容
strcpy(m_str, other.m_str); //複製other.m_str到私有成員m_str中
}
//賦值函數
String & String::operator=(String &other){
if(this == &other) //若對象與other是同一個對象,直接返回本{
return *this
}
delete [] m_str; //不然,先釋放當前對象堆內存
m_str = new char[strlen(other.m_str)+1]; //分配空間容納str內容
strcpy(m_str, other.m_str); //複製other.m_str到私有成員m_str中
return *this;
}
26. C語言贊成一些使人震驚的結構,下面的結構是合法的嗎,若是是它作些什麼?
int a = 5, b = 7, c;
c = a+++b; 等同於 c = a++ + b;
所以, 這段代碼持行後a = 6, b = 7, c = 12。
27. 用struct關鍵字與class關鍵定義類以及繼承的區別
(1)定義類差異
struct關鍵字也能夠實現類,用class和struct關鍵字定義類的惟一差異在於默認訪問級別:默認狀況下,struct成員的訪問級別爲public,而class成員的爲private。語法使用也相同,直接將class改成struct便可。
(2)繼承差異
使用class保留字的派生類默認具備private繼承,而用struct保留字定義的類某人具備public繼承。其它則沒有任何區別。
主要點就兩個:默認的訪問級別和默認的繼承級別 class都是private
28.派生類與虛函數概述
(1) 派生類繼承的函數不能定義爲虛函數。虛函數是但願派生類從新定義。若是派生類沒有從新定義某個虛函數,則在調用的時候會使用基類中定義的版本。
(2)派生類中函數的聲明必須與基類中定義的方式徹底匹配。
(3) 基類中聲明爲虛函數,則派生類也爲虛函數。
29. 虛函數與純虛函數區別
1)虛函數在子類裏面也能夠不重載的;但純虛必須在子類去實現
2)帶純虛函數的類叫虛基類也叫抽象類,這種基類不能直接生成對象,只能被繼承,重寫虛函數後才能使用,運行時動態動態綁定!
30.深拷貝與淺拷貝
淺拷貝:
char ori[]=「hello」;char *copy=ori;
深拷貝:
char ori[]="hello"; char *copy=new char[]; copy=ori;
淺拷貝只是對指針的拷貝,拷貝後兩個指針指向同一個內存空間,深拷貝不但對指針進行拷貝,並且對指針指向的內容進行拷貝,經深拷貝後的指針是指向兩個不一樣地址的指針。
淺拷貝可能出現的問題:
1) 淺拷貝只是拷貝了指針,使得兩個指針指向同一個地址,這樣在對象塊結束,調用函數析構的時,會形成同一份資源析構2次,即delete同一塊內存2次,形成程序崩潰。
2) 淺拷貝使得兩個指針都指向同一塊內存,任何一方的變更都會影響到另外一方。
3) 同一個空間,第二次釋放失敗,致使沒法操做該空間,形成內存泄漏。
31. stl各容器的實現原理(必考)
1) Vector順序容器,是一個動態數組,支持隨機插入、刪除、查找等操做,在內存中是一塊連續的空間。在原有空間不夠狀況下自動分配空間,增長爲原來的兩倍。vector隨機存取效率高,可是在vector插入元素,須要移動的數目多,效率低下。
注:vector動態增長大小時是以原大小的兩倍另外配置一塊較大的空間,而後將原內容拷貝過來,而後纔開始在原內容以後構造新元素,並釋放原空間。所以,對vector空間從新配置,指向原vector的全部迭代器就都失效了。
2) Map關聯容器,以鍵值對的形式進行存儲,方便進行查找。關鍵詞起到索引的做用,值則表示與索引相關聯的數據。紅黑樹的結構實現,插入刪除等操做都在O(logn)時間內完成。
3) Set是關聯容器,set每一個元素只包含一個關鍵字。set支持高效的關鍵字檢查是否在set中。set也是以紅黑樹的結構實現,支持高效插入、刪除等操做。
32.哪些庫函數屬於高危函數,爲何?
strcpy 賦值到目標區間可能會形成緩衝區溢出!
33.STL有7種主要容器:vector,list,deque,map,multimap,set,multiset
34.你如何理解MVC。簡單舉例來講明其應用。
MVC模式是observer 模式的一個特例,如今不少都是java的一些框架,MFC的,PHP的。
35.C++特色是什麼,多態實現機制?(面試問過)多態做用?兩個必要條件?
C++中多態機制主要體如今兩個方面,一個是函數的重載,一個是接口的重寫。接口多態指的是「一個接口多種形態」。每個對象內部都有一個虛表指針,該虛表指針被初始化爲本類的虛表。因此在程序中,無論你的對象類型如何轉換,但該對象內部的虛表指針是固定的,因此呢,才能實現動態的對象函數調用,這就是C++多態性實現的原理。
多態的基礎是繼承,須要虛函數的支持,簡單的多態是很簡單的。子類繼承父類大部分的資源,不能繼承的有構造函數,析構函數,拷貝構造函數,operator=函數,友元函數等等
做用:
- 隱藏實現細節,代碼可以模塊化;2. 接口重用:爲了類在繼承和派生的時候正確調用。
必要條件:
1. 一個基類的指針或者引用指向派生類的對象;2.虛函數
36. 多重繼承有什麼問題? 怎樣消除多重繼承中的二義性?
1)增長程序的複雜度,使程序的編寫和維護比較困難,容易出錯;
2)繼承類和基類的同名函數產生了二義性,同名函數不知道調用基類仍是繼承類,C++中使用虛函數解決這個問題
3)繼承過程當中可能會繼承一些沒必要要的數據,對於多級繼承,可能會產生數據很長
能夠使用成員限定符和虛函數解決多重繼承中函數的二義性問題。
37.求兩個數的乘積和商數,該做用由宏定義來實現
#define product(a,b) ((a)*(b))
#define divide(a,b) ((a)/(b))
38.什麼叫靜態關聯,什麼叫動態關聯
多態中,靜態關聯是程序在編譯階段就能肯定實際執行動做,程序運行才能肯定叫動態關聯
39.什麼叫智能指針?經常使用的智能指針有哪些?智能指針的實現?
智能指針是一個存儲指向動態分配(堆)對象指針的類,構造函數傳入普通指針,析構函數釋放指針。棧上分配,函數或程序結束自動釋放,防止內存泄露。使用引用計數器,類與指向的對象相關聯,引用計數跟蹤該類有多少個對象共享同一指針。建立類的新對象時,初始化指針並將引用計數置爲1;當對象做爲另外一對象的副本而建立,增長引用計數;對一個對象進行賦值時,減小引用計數,並增長右操做數所指對象的引用計數;調用析構函數時,構造函數減小引用計數,當引用計數減至0,則刪除基礎對象。
std::auto_ptr,不支持複製(拷貝構造函數)和賦值(operator =),編譯不會提示出錯。
C++11引入的unique_ptr, 也不支持複製和賦值,但比auto_ptr好,直接賦值會編譯出錯。
C++11或boost的shared_ptr,基於引用計數的智能指針。可隨意賦值,直到內存的引用計數爲0的時候這個內存會被釋放。還有Weak_ptr
40.枚舉與#define 宏的區別
1)#define 宏常量是在預編譯階段進行簡單替換。枚舉常量則是在編譯的時候肯定其值。
2)能夠調試枚舉常量,可是不能調試宏常量。
3)枚舉能夠一次定義大量相關的常量,而#define 宏一次只能定義一個。
41.介紹一下函數的重載
重載是在不一樣類型上做不一樣運算而又用一樣的名字的函數。重載函數至少在參數個數,參數類型, 或參數順序上有所不一樣。
42.派生新類的過程要經歷三個步驟
1.吸取基類成員 2.改造基類成員 3.添加新成員
43.面向對象的三個基本特徵,並簡單敘述之?
1)封裝:將客觀事物抽象成類,每一個類對自身的數據和方法實行2)繼承3)多態:容許一個基類的指針或引用指向一個派生類對象
44.多態性體現都有哪些?動態綁定怎麼實現?
多態性是一個接口,多種實現,是面向對象的核心。 編譯時多態性:經過重載函數實現。運行時多態性:經過虛函數實現,結合動態綁定。
45.虛函數,虛函數表裏面內存如何分配?
編譯時若基類中有虛函數,編譯器爲該的類建立一個一維數組的虛表,存放是每一個虛函數的地址。基類和派生類都包含虛函數時,這兩個類都創建一個虛表。構造函數中進行虛表的建立和虛表指針的初始化。在構造子類對象時,要先調用父類的構造函數,初始化父類對象的虛表指針,該虛表指針指向父類的虛表。執行子類的構造函數時,子類對象的虛表指針被初始化,指向自身的虛表。每個類都有虛表。虛表能夠繼承,若是子類沒有重寫虛函數,那麼子類虛表中仍然會有該函數的地址,只不過這個地址指向的是基類的虛函數實現。派生類的虛表中虛函數地址的排列順序和基類的虛表中虛函數地址排列順序相同。當用一個指針/引用調用一個函數的時候,被調用的函數是取決於這個指針/引用的類型。即若是這個指針/引用是基類對象的指針/引用就調用基類的方法;若是指針/引用是派生類對象的指針/引用就調用派生類的方法,固然若是派生類中沒有此方法,就會向上到基類裏面去尋找相應的方法。這些調用在編譯階段就肯定了。當涉及到多態性的時候,採用了虛函數和動態綁定,此時的調用就不會在編譯時候肯定而是在運行時肯定。不在單獨考慮指針/引用的類型而是看指針/引用的對象的類型來判斷函數的調用,根據對象中虛指針指向的虛表中的函數的地址來肯定調用哪一個函數。
46. 純虛函數如何定義?含有純虛函數的類稱爲何?爲何析構函數要定義成虛函數?
純虛函數是在基類中聲明的虛函數,它在基類中沒有定義,但要求任何派生類都要定義本身的實現方法。純虛函數是虛函數再加上= 0。virtual void fun ()=0。含有純虛函數的類稱爲抽象類在不少狀況下,基類自己生成對象是不合情理的。例如,動物做爲一個基類能夠派生出老虎、孔雀等子類,但動物自己生成對象明顯不合常理。同時含有純虛擬函數的類稱爲抽象類,它不能生成對象。若是析構函數不是虛函數,那麼釋放內存時候,編譯器會使用靜態聯編,認爲p就是一個基類指針,調用基類析構函數,這樣子類對象的內存沒有釋放,形成內存泄漏。定義成虛函數之後,就會動態聯編,先調用子類析構函數,再基類。
47. C++中哪些不能是虛函數?
1)普通函數只能重載,不能被重寫,所以編譯器會在編譯時綁定函數。
2)構造函數是知道所有信息才能建立對象,然而虛函數容許只知道部分信息。
3)內聯函數在編譯時被展開,虛函數在運行時才能動態綁定函數。
4)友元函數 由於不能夠被繼承。
5)靜態成員函數 只有一個實體,不能被繼承。父類和子類共有。
48. 類型轉換有哪些?各適用什麼環境?dynamic_cast轉換失敗時,會出現什麼狀況(對指針,返回NULL.對引用,拋出bad_cast異常)?
靜態類型轉換,static_cast,基本類型之間和具備繼承關係的類型。
例子A,double類型轉換成int。B,將子類對象轉換成基類對象。
常量類型轉換,const_cast, 去除指針變量的常量屬性。
沒法將非指針的常量轉換爲普通變量。
動態類型轉換,dynamic_cast,運行時進行轉換分析的,並不是在編譯時進行。dynamic_cast轉換符只能用於含有虛函數的類。dynamic_cast用於類層次間的向上轉換和向下轉換,還能夠用於類間的交叉轉換。在類層次間進行向上轉換,即子類轉換爲父類,此時完成的功能和static_cast是相同的,由於編譯器默認向上轉換老是安全的。向下轉換時,dynamic_cast具備類型檢查的功能,更加安全。類間的交叉轉換指的是子類的多個父類之間指針或引用的轉換。該函數只能在繼承類對象的指針之間或引用之間進行類型轉換,或者有虛函數的類。
49. 如何判斷一段程序是由C 編譯程序仍是由C++編譯程序編譯的?
#ifdef __cplusplus
cout<<"C++";
#else
cout<<"c";
#endif
50. 爲何要用static_cast轉換而不用c語言中的轉換?
Static_cast轉換,它會檢查類型看是否能轉換,有類型安全檢查。
好比,這個在C++中合法,可是確實錯誤的。
A* a= new A;
B* b = (B*)a;
51. 操做符重載(+操做符),具體如何去定義?
除了類屬關係運算符」.」、成員指針運算符」.*」、做用域運算符」::」、sizeof運算符和三目運算符」?:」之外,C++中的全部運算符均可以重載。
<返回類型說明符> operator <運算符符號>(<參數表>){}
重載爲類的成員函數和重載爲類的非成員函數。參數個數會不一樣,應爲this指針。
52. 內存對齊的原則?
A.結構體的大小爲最大成員的整數倍。
B.成員首地址的偏移量爲其類型大小整數倍。
53. 內聯函數與宏定義的區別?
內聯函數是用來消除函數調用時的時間開銷。頻繁被調用的短小函數很是受益。
A. 宏定義不檢查函數參數,返回值什麼的,只是展開,相對來講,內聯函數會檢查參數類型,因此更安全。
B. 宏是由預處理器對宏進行替代,而內聯函數是經過編譯器控制來實現的
54. 動態分配對象和靜態分配對象的區別?
動態分配就是用運算符new來建立一個類的對象,在堆上分配內存。
靜態分配就是A a;這樣來由編譯器來建立一個對象,在棧上分配內存。
55. explicit是幹什麼用的 ?
構造器 ,能夠阻止不該該容許的通過轉換構造函數進行的隱式轉換的發生。explicit是用來防止外部非正規的拷貝構造的,要想不存在傳值的隱式轉換問題。
56. 內存溢出有那些因素?
(1) 使用非類型安全(non-type-safe)的語言如 C/C++ 等。
(2) 以不可靠的方式存取或者複製內存緩衝區。
(3) 編譯器設置的內存緩衝區太靠近關鍵數據結構。
57. new與malloc的區別,delete和free的區別?
1.malloc/free是C/C++語言的標準庫函數,new/delete是C++的運算符
2.new可以自動分配空間大小,malloc傳入參數。
3. new/delete能進行對對象進行構造和析構函數的調用進而對內存進行更加詳細的工做,而malloc/free不能。
既然new/delete的功能徹底覆蓋了malloc/free,爲何C++還保留malloc/free呢?由於C++程序常常要調用C函數,而C程序只能用malloc/free管理動態內存。
58. 必須使用初始化列表初始化數據成員的狀況
1.是對象的狀況;
2.const修飾的類成員;
3.引用成員數據;
類成員變量的初始化不是按照初始化表順序被初始化,是按照在類中聲明的順序被初始化的。
59.深刻談談堆和棧
1).分配和管理方式不一樣 :
堆是動態分配的,其空間的分配和釋放都由程序員控制。
棧由編譯器自動管理。棧有兩種分配方式:靜態分配和動態分配。靜態分配由編譯器完成,好比局部變量的分配。動態分配由alloca()函數進行分配,可是棧的動態分配和堆是不一樣的,它的動態分配是由編譯器進行釋放,無須手工控制。
2).產生碎片不一樣
對堆來講,頻繁的new/delete或者malloc/free勢必會形成內存空間的不連續,形成大量的碎片,使程序效率下降。
對棧而言,則不存在碎片問題,由於棧是先進後出的隊列,永遠不可能有一個內存塊從棧中間彈出。
3).生長方向不一樣
堆是向着內存地址增長的方向增加的,從內存的低地址向高地址方向增加。
棧是向着內存地址減少的方向增加,由內存的高地址向低地址方向增加。
60.內存的靜態分配和動態分配的區別?
時間不一樣。靜態分配發生在程序編譯和鏈接時。動態分配則發生在程序調入和執行時。
空間不一樣。堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的,好比局部變量的分配。alloca,能夠從棧裏動態分配內存,不用擔憂內存泄露問題,當函數返回時,經過alloca申請的內存就會被自動釋放掉。
61. 模版怎麼實現?模版做用?
實現:template void swap(T& a, T& b){}
做用:將算法與具體對象分離,與類型無關,通用,節省精力
62. 多重類構造和析構的順序
記住析構函數的調用順序與構造函數是相反的。
63. 迭代器刪除元素的會發生什麼?
迭代器失效
64. 靜態成員函數和數據成員有什麼意義?
1)非靜態數據成員,每一個對象都有本身的拷貝。而靜態數據成員被看成是類的成員,是該類的全部對象所共有的,在程序中只分配一次內存只有一份拷貝,因此對象都共享,值對每一個對象都是同樣的,它的值能夠更新。
2)靜態數據成員存儲在全局數據區,因此不能在類聲明中定義,應該在類外定義。因爲它不屬於特定的類對象,在沒有產生類對象時做用域就可見,即在沒有產生類的實例時,咱們就能夠操做它。
3)靜態成員函數與靜態數據成員同樣,都是在類的內部實現,屬於類定義的一部分。由於普通成員函數老是具體的屬於具體對象的,每一個有this指針。靜態成員函數沒有this指針,它沒法訪問屬於類對象的非靜態數據成員,也沒法訪問非靜態成員函數。靜態成員之間能夠互相訪問,包括靜態成員函數訪問靜態數據成員和訪問靜態成員函數;
4)非靜態成員函數能夠任意地訪問靜態成員函數和靜態數據成員;
5)沒有this指針的額外開銷,靜態成員函數與類的全局函數相比,速度上會有少量的增加;
6)調用靜態成員函數,能夠用成員訪問操做符(.)和(->)爲一個類的對象或指向類對象的指調用靜態成員函數。
65.sizeof一個類求大小(注意成員變量,函數,虛函數,繼承等等對大小的影響)
http://blog.csdn.net/jollyhope/article/details/1895357
http://www.cnblogs.com/BeyondTechnology/archive/2010/09/21/1832369.html
66請用C/C++實現字符串反轉(不調用庫函數)」abc」類型的
char *reverse_str(char *str) {
if(NULL == str) { //字符串爲空直接返回
return str;
}
char *begin;
char *end;
begin = end = str;
while(*end != '\0') { //end指向字符串的末尾
end++;
}
--end;
char temp;
while(begin < end) { //交換兩個字符
temp = *begin;
*begin = *end;
*end = temp;
begin++;
end--;
}
return str; //返回結果
}
67.寫一個函數,將字符串翻轉,翻轉方式以下:「I am a student」反轉成「student a am I」,不借助任何庫函數
1 #include "stdio.h"
2 #include <iostream>
3 using namespace std;
4
5 void revesal(char * start, char* end){
6 char *temp_s = start;
7 char *temp_e = end;
8 while(temp_s < temp_e){
9 char temp= *temp_s;
10 *temp_s= *temp_e;
11 *temp_e = temp;
12 ++temp_s;
13 --temp_e;
14 }
15 return;
16 }
17
18 void revesal_str(char *str){
19 if(str == NULL){
20 return;
21 }
22
23 char *start = str;
24 char *end = str;
25
26 while(*++end !='\0');
27 revesal(start, end-1);
28 cout << str << endl;
29 char *sub_start = str;
30 while(start < end + 1 ){
31 if(*start == ' ' || *start == '\0'){
32 char *temp = start - 1;
33 revesal(sub_start,temp);
34 while(*++start ==' ');
35 sub_start = start;
36 continue;
37 }
38 ++start;
39 }
40 }
68.析構函數能夠拋出異常嗎?爲何不能拋出異常?除了資源泄露,還有其餘需考慮的因素嗎?
C++標準指明析構函數不能、也不該該拋出異常。C++異常處理模型最大的特色和優點就是對C++中的面向對象提供了最強大的無縫支持。那麼若是對象在運行期間出現了異常,C++異常處理模型有責任清除那些因爲出現異常所致使的已經失效了的對象(也即對象超出了它原來的做用域),並釋放對象原來所分配的資源, 這就是調用這些對象的析構函數來完成釋放資源的任務,因此從這個意義上說,析構函數已經變成了異常處理的一部分。
1)若是析構函數拋出異常,則異常點以後的程序不會執行,若是析構函數在異常點以後執行了某些必要的動做好比釋放某些資源,則這些動做不會執行,會形成諸如資源泄漏的問題。
2)一般異常發生時,c++的機制會調用已經構造對象的析構函數來釋放資源,此時若析構函數自己也拋出異常,則前一個異常還沒有處理,又有新的異常,會形成程序崩潰的問題。
69. 拷貝構造函數做用及用途?何時須要自定義拷貝構造函數?
通常若是構造函數中存在動態內存分配,則必須定義拷貝構造函數。不然,可能會致使兩個對象成員指向同一地址,出現「指針懸掛問題」。
70. 100萬個32位整數,如何最快找到中位數。能保證每一個數是惟一的,如何實現O(N)算法?
1).內存足夠時:快排
2).內存不足時:分桶法:化大爲小,把全部數劃分到各個小區間,把每一個數映射到對應的區間裏,對每一個區間中數的個數進行計數,數一遍各個區間,看看中位數落在哪一個區間,若夠小,使用基於內存的算法,不然 繼續劃分
71. OFFSETOF(s, m)的宏定義,s是結構類型,m是s的成員,求m在s中的偏移量。
#define OFFSETOF(s, m) size_t(&((s*)0)->m)
72. C++虛函數是如何實現的?
使用虛函數表。 C++對象使用虛表, 若是是基類的實例,對應位置存放的是基類的函數指針;若是是繼承類,對應位置存放的是繼承類的函數指針(若是在繼承類有實現)。因此 ,當使用基類指針調用對象方法時,也會根據具體的實例,調用到繼承類的方法。
73. C++的虛函數有什麼做用?
虛函數做用是實現多態,虛函數實際上是實現封裝,使得使用者不須要關心實現的細節。在不少設計模式中都是這樣用法,例如Factory、Bridge、Strategy模式。
74.MFC中CString是類型安全類嗎,爲何?
不是,其餘數據類型轉換到CString能夠使用CString的成員函數Format來轉換
74.動態連接庫的兩種使用方法及特色?
1).載入時動態連接,模塊很是明確調用某個導出函數,使得他們就像本地函數同樣。這須要連接時連接那些函數所在DLL的導入庫,導入庫向系統提供了載入DLL時所需的信息及DLL函數定位。
2)運行時動態連接。
2、服務器編程
1.多線程和多進程的區別(重點 必須從cpu調度,上下文切換,數據共享,多核cup利用率,資源佔用,等等各方面回答,而後有一個問題必須會被問到:哪些東西是一個線程私有的?答案中必須包含寄存器,不然悲催)!
1)進程數據是分開的:共享複雜,須要用IPC,同步簡單;多線程共享進程數據:共享簡單,同步複雜
2)進程建立銷燬、切換複雜,速度慢 ;線程建立銷燬、切換簡單,速度快
3)進程佔用內存多, CPU利用率低;線程佔用內存少, CPU利用率高
4)進程編程簡單,調試簡單;線程 編程複雜,調試複雜
5)進程間不會相互影響 ;線程一個線程掛掉將致使整個進程掛掉
6)進程適應於多核、多機分佈;線程適用於多核
線程所私有的:
線程id、寄存器的值、棧、線程的優先級和調度策略、線程的私有數據、信號屏蔽字、errno變量、
2. 多線程鎖的種類有哪些?
a.互斥鎖(mutex)b.遞歸鎖 c.自旋鎖 d.讀寫鎖
3. 自旋鎖和互斥鎖的區別?
當鎖被其餘線程佔用時,其餘線程並非睡眠狀態,而是不停的消耗CPU,獲取鎖;互斥鎖則否則,保持睡眠,直到互斥鎖被釋放激活。
自旋鎖,遞歸調用容易形成死鎖,對長時間才能得到到鎖的狀況,使用自旋鎖容易形成CPU效率低,只有內核可搶佔式或SMP狀況下才真正須要自旋鎖。
4.進程間通訊和線程間通訊
1).管道 2)消息隊列 3)共享內存 4)信號量 5)套接字 6)條件變量
5.多線程程序架構,線程數量應該如何設置?
應儘可能和CPU核數相等或者爲CPU核數+1的個數
6.什麼是原子操做,gcc提供的原子操做原語,使用這些原語如何實現讀寫鎖?
原子操做是指不會被線程調度機制打斷的操做;這種操做一旦開始,就一直運行到結束,中間不會有任何 context switch。
7.網絡編程設計模式,reactor/proactor/半同步半異步模式?
reactor模式:同步阻塞I/O模式,註冊對應讀寫事件處理器,等待事件發生進而調用事件處理器處理事件。 proactor模式:異步I/O模式。Reactor和Proactor模式的主要區別就是真正的讀取和寫入操做是有誰來完成的,Reactor中須要應用程序本身讀取或者寫入數據,Proactor模式中,應用程序不須要進行實際讀寫過程。
Reactor是:
主線程往epoll內核上註冊socket讀事件,主線程調用epoll_wait等待socket上有數據可讀,當socket上有數據可讀的時候,主線程把socket可讀事件放入請求隊列。睡眠在請求隊列上的某個工做線程被喚醒,處理客戶請求,而後往epoll內核上註冊socket寫請求事件。主線程調用epoll_wait等待寫請求事件,當有事件可寫的時候,主線程把socket可寫事件放入請求隊列。睡眠在請求隊列上的工做線程被喚醒,處理客戶請求。
Proactor:
主線程調用aio_read函數向內核註冊socket上的讀完成事件,並告訴內核用戶讀緩衝區的位置,以及讀完成後如何通知應用程序,主線程繼續處理其餘邏輯,當socket上的數據被讀入用戶緩衝區後,經過信號告知應用程序數據已經能夠使用。應用程序預先定義好的信號處理函數選擇一個工做線程來處理客戶請求。工做線程處理完客戶請求以後調用aio_write函數向內核註冊socket寫完成事件,並告訴內核寫緩衝區的位置,以及寫完成時如何通知應用程序。主線程處理其餘邏輯。當用戶緩存區的數據被寫入socket以後內核嚮應用程序發送一個信號,以通知應用程序數據已經發送完畢。應用程序預先定義的數據處理函數就會完成工做。
半同步半異步模式:
上層的任務(如:數據庫查詢,文件傳輸)使用同步I/O模型,簡化了編寫並行程序的難度。
而底層的任務(如網絡控制器的中斷處理)使用異步I/O模型,提供了執行效率。
8.有一個計數器,多個線程都須要更新,會遇到什麼問題,緣由是什麼,應該如何作?如何優化?
有可能一個線程更新的數據已經被另一個線程更新了,更新的數據就會出現異常,能夠加鎖,保證數據更新只會被一個線程完成。
9.若是select返回可讀,結果只讀到0字節,什麼狀況?
某個套接字集合中沒有準備好,可能會select內存用FD_CLR清爲0.
10. connect可能會長時間阻塞,怎麼解決?
1.使用定時器;(最經常使用也最有效的一種方法)
2.採用非阻塞模式:設置非阻塞,返回以後用select檢測狀態。
11.keepalive 是什麼東西?如何使用?
keepalive,是在TCP中一個能夠檢測死鏈接的機制。
1).若是主機可達,對方就會響應ACK應答,就認爲是存活的。
2).若是可達,但應用程序退出,對方就發RST應答,發送TCP撤消鏈接。
3).若是可達,但應用程序崩潰,對方就發FIN消息。
4).若是對方主機不響應ack, rst,繼續發送直到超時,就撤消鏈接。默認二個小時。
12.socket什麼狀況下可讀?
1.socket接收緩衝區中已經接收的數據的字節數大於等於socket接收緩衝區低潮限度的當前值;對這樣的socket的讀操做不會阻塞,並返回一個大於0的值(準備好讀入的數據的字節數).
2.鏈接的讀一半關閉(即:接收到對方發過來的FIN的TCP鏈接),而且返回0;
3.socket收到了對方的connect請求已經完成的鏈接數爲非0.這樣的soocket處於可讀狀態;
4.異常的狀況下socket的讀操做將不會阻塞,而且返回一個錯誤(-1)。
13.udp調用connect有什麼做用?
1).由於UDP能夠是一對一,多對一,一對多,或者多對多的通訊,因此每次調用sendto()/recvfrom()時都必須指定目標IP和端口號。經過調用connect()創建一個端到端的鏈接,就能夠和TCP同樣使用send()/recv()傳遞數據,而不須要每次都指定目標IP和端口號。可是它和TCP不一樣的是它沒有三次握手的過程。
2).能夠經過在已創建鏈接的UDP套接字上,調用connect()實現指定新的IP地址和端口號以及斷開鏈接。
14. socket編程,若是client斷電了,服務器如何快速知道?
使用定時器(適合有數據流動的狀況);
使用socket選項SO_KEEPALIVE(適合沒有數據流動的狀況);
1)、本身編寫心跳包程序,簡單的說就是本身的程序加入一條線程,定時向對端發送數據包,查看是否有ACK,根據ACK的返回狀況來管理鏈接。此方法比較通用,通常使用業務層心跳處理,靈活可控,但改變了現有的協議;
2)、使用TCP的keepalive機制,UNIX網絡編程不推薦使用SO_KEEPALIVE來作心)跳檢測。
keepalive原理:TCP內嵌有心跳包,以服務端爲例,當server檢測到超過必定時間(/proc/sys/net/ipv4/tcp_keepalive_time 7200 即2小時)沒有數據傳輸,那麼會向client端發送一個keepalive packet。
3、liunx操做系統
1.熟練netstat tcpdump ipcs ipcrm
netstat:檢查網絡狀態,tcpdump:截獲數據包,ipcs:檢查共享內存,ipcrm:解除共享內存
2.共享內存段被映射進進程空間以後,存在於進程空間的什麼位置?共享內存段最大限制是多少?
將一塊內存映射到兩個或者多個進程地址空間。經過指針訪問該共享內存區。通常經過mmap將文件映射到進程地址共享區。
存在於進程數據段,最大限制是0x2000000Byte
3.進程內存空間分佈狀況
4.ELF是什麼?其大小與程序中全局變量的是否初始化有什麼關係(注意未初始化的數據放在bss段)
可執行鏈接格式。能夠減小從新編程從新編譯的代碼。
5.動態連接和靜態連接的區別?
動態連接是隻創建一個引用的接口,而真正的代碼和數據存放在另外的可執行模塊中,在可執行文件運行時再裝入;而靜態連接是把全部的代碼和數據都複製到本模塊中,運行時就再也不須要庫了
6.32位系統一個進程最多有多少堆內存
32位意味着4G的尋址空間,Linux把它分爲兩部分:最高的1G(虛擬地址從0xC0000000到0xffffffff)用作內核自己,成爲「系統空間」,而較低的3G字節(從0x00000000到0xbffffff)用做各進程的「用戶空間」。每一個進程能夠使用的用戶空間是3G。雖然各個進程擁有其本身的3G用戶空間,系統空間卻由全部的進程共享。從具體進程的角度看,則每一個進程都擁有4G的虛擬空間,較低的3G爲本身的用戶空間,最高的1G爲全部進程以及內核共享的系統空間。實際上有人作過測試也就2G左右。
7.寫一個c程序辨別系統是64位 or 32位
void* number = 0; printf("%d\n",sizeof(&number));
輸出8就是64位 輸出4就是32位的 根據邏輯地址判斷的
8.寫一個c程序辨別系統是大端or小端字節序
union{ short value; char a[sizeof(short)];}test;
test.value= 0x0102;
if((test.a[0] == 1) && (test.a[1] == 2)) cout << "big"<<endl; else cout << "little" << endl;
9.信號:列出常見的信號,信號怎麼處理?
1).進程終止的信號 2).跟蹤進程的信號 3).與進程例外事件相關的信號等
對於信號的處理或者執行相關的操做進行處理或者直接忽略
10.i++ 是否原子操做?並解釋爲何?
答案確定不是原子操做,i++主要看三個步驟
首先把數據從內存放到寄存器上,在寄存器上進行自增處理,放回到寄存器上,每一個步驟均可能會被中斷分離開!
11.說出你所知道的各種linux系統的各種同步機制(重點),什麼是死鎖?如何避免死鎖(每一個技術面試官必問)
1).原子操做 2).信號量(其實就是互斥鎖也就是鎖的機制)3).讀寫信號量(就是讀寫鎖) 4).自旋鎖 5.內核鎖 6).順序鎖
死鎖就是幾個進程申請資源,出現了循環等待的狀況!
避免死鎖的方法:
1).資源是互斥的 2).不可搶佔 3)佔有且申請 4).循環等待
12、exit() _exit()的區別?
13、如何實現守護進程?
1)建立子進程,父進程退出
2)在子進程中建立新會話
3)改變當前目錄爲根目
4)重設文件權限掩碼
5) 關閉文件描述符
6) 守護進程退出處理
當用戶須要外部中止守護進程運行時,每每會使用 kill命令中止該守護進程。因此,守護進程中須要編碼來實現kill發出的signal信號處理,達到進程的正常退出。
14、linux的任務調度機制是什麼?
Linux 分實時進程和普通進程,實時進程應該先於普通進程而運行。實時進程:
1) FIFO(先來先服務調度)
2) RR(時間片輪轉調度)。
每一個進程有兩個優先級(動態優先級和實時優先級),實時優先級就是用來衡量實時進程是否值得運行的。 非實時進程有兩種優先級,一種是靜態優先級,另外一種是動態優先級。實時進程又增長了第三種優先級,實時優先級。優先級越高,獲得CPU時間的機會也就越大。
15、標準庫函數和系統調用的區別?
系統調用:是操做系統爲用戶態運行的進程和硬件設備(如CPU、磁盤、打印機等)進行交互提供的一組接口,即就是設置在應用程序和硬件設備之間的一個接口層。inux內核是單內核,結構緊湊,執行速度快,各個模塊之間是直接調用的關係。linux系統上到下依次是用戶進程->linux內核->硬件。其中系統調用接口是位於Linux內核中的,整個linux系統從上到下能夠是:用戶進程->系統調用接口->linux內核子系統->硬件,也就是說Linux內核包括了系統調用接口和內核子系統兩部分;或者從下到上能夠是:物理硬件->OS內核->OS服務->應用程序,操做系統起到「承上啓下」做用,向下管理物理硬件,向上爲操做系服務和應用程序提供接口,這裏的接口就是系統調用了。
庫函數:把函數放到庫裏。是把一些經常使用到的函數編完放到一個lib文件裏,供別人用。別人用的時候把它所在的文件名用#include<>加到裏面就能夠了。一類是c語言標準規定的庫函數,一類是編譯器特定的庫函數。
系統調用是爲了方便使用操做系統的接口,而庫函數則是爲了人們編程的方便。
16、系統如何將一個信號通知到進程?
內核給進程發送信號,是在進程所在的進程表項的信號域設置對應的信號的位。進程處理信號的時機就是從內核態即將返回用戶態度的時候。執行用戶自定義的信號處理函數的方法很巧妙。把該函數的地址放在用戶棧棧頂,進程從內核返回到用戶態的時候,先彈出信號處理函數地址,因而就去執行信號處理函數了,而後再彈出,纔是返回進入內核時的狀態。
17. fork()一子進程程後父進程的全局變量能不能使用?
fork後子進程將會擁有父進程的幾乎一切資源,父子進程的都各自有本身的全局變量。不能通用,不一樣於線程。對於線程,各個線程共享全局變量。
18. 請畫出socket通訊鏈接過程
19. 請用socket消息隊列實現「同步非阻塞」和「異步阻塞」兩種模式,並指出二者的差異和優劣
http://blog.csdn.net/yongchurui/article/details/12780653
4、網絡編程
1. TCP頭大小,包含字段?三次握手,四次斷開描述過程,都有些什麼狀態。狀態變遷圖。TCP/IP收發緩衝區(2次)
頭部大小是20字節,包含數據以下:
三次握手:
四次釋放:
狀態變遷圖:
收發緩衝區:
2. 使用udp和tcp進程網絡傳輸,爲何tcp能保證包是發送順序,而 udp沒法保證?
由於TCP發送的數據包是按序號發送,有確認機制和丟失重傳機制,而udp是不可靠的發送機制,發送的對應端口的數據包不是按順序發送的。
3. epoll哪些觸發模式,有啥區別?(必須很是詳盡的解釋水平觸發和邊緣觸發的區別,以及邊緣觸發在編程中要作哪些更多的確認)
epoll有EPOLLLT和EPOLLET兩種觸發模式,LT是默認的模式,ET是「高速」模式。LT模式下,只要這個fd還有數據可讀,每次 epoll_wait都會返回它的事件,提醒用戶程序去操做,而在ET(邊緣觸發)模式中,它只會提示一次,直到下次再有數據流入以前都不會再提示了,不管fd中是否還有數據可讀。因此在ET模式下,read一個fd的時候必定要把它的buffer讀光,也就是說一直讀到read的返回值小於請求值。
也就是說在LT模式的狀況下必定要確認收發的數據包的buffer是否是足夠大若是收發數據包大小大於buffer的大小的時候就可能會出現數據丟失的狀況。
4. tcp與udp的區別(必問)爲何TCP要叫作數據流?
1).基於鏈接與無鏈接
2).對系統資源的要求(TCP較多,UDP少)
3).UDP程序結構較簡單
4).流模式與數據報模式
5).TCP保證數據正確性,UDP可能丟包,TCP保證數據順序,UDP不保證
6).TCP有擁塞控制和流量控制,UDP沒有
TCP提供的是面向鏈接、可靠的字節流服務。當客戶和服務器彼此交換數據前,必須先在雙方之間創建一個TCP鏈接,以後才能傳輸數據。TCP提供超時重發,丟棄重複數據,檢驗數據,流量控制等功能,保證數據能從一端傳到另外一端。
是一個簡單的面向數據報的運輸層協議。UDP不提供可靠性,它只是把應用程序傳給IP層的數據報發送出去,可是並不能保證它們能到達目的地。因爲UDP在傳輸數據報前不用在客戶和服務器之間創建一個鏈接,且沒有超時重發等機制,故而傳輸速度很快
5.流量控制和擁塞控制的實現機制
網絡擁塞現象是指到達通訊子網中某一部分的分組數量過多,使得該部分網絡來不及處理,以至引發這部分乃至整個網絡性能降低的現象,嚴重時甚至會致使網絡通訊業務陷入停頓,即出現死鎖現象。擁塞控制是處理網絡擁塞現象的一種機制。數據的傳送與接收過程中極可能出現收方來不及接收的狀況,這時就須要對發方進行控制,以避免數據丟失。
6. 滑動窗口的實現機制
滑動窗口機制,窗口的大小並非固定的而是根據咱們之間的鏈路的帶寬的大小,這個時候鏈路是否擁護塞。接受方是否能處理這麼多數據了。 滑動窗口協議,是TCP使用的一種流量控制方法。該協議容許發送方在中止並等待確認前能夠連續發送多個分組。因爲發送方沒必要每發一個分組就停下來等待確認,所以該協議能夠加速數據的傳輸。
7.epoll和select的區別?
1)select在一個進程中打開的最大fd是有限制的,由FD_SETSIZE設置,默認值是2048。不過 epoll則沒有這個限制,內存越大,fd上限越大,1G內存都能達到大約10w左右。
2)select的輪詢機制是系統會去查找每一個fd是否數據已準備好,當fd不少的時候,效率固然就直線降低了,epoll採用基於事件的通知方式,一旦某個fd數據就緒時,內核會採用相似callback的回調機制,迅速激活這個文件描述符,高效。
3)select仍是epoll都須要內核把FD消息通知給用戶空間,epoll是經過內核於用戶空間mmap同一塊內存實現的,而select則作了沒必要要的拷貝
8. 網絡中,若是客戶端忽然掉線或者重啓,服務器端怎麼樣才能馬上知道?
若客戶端掉線或者從新啓動,服務器端會收到復位信號,每一種tcp/ip得實現不同,控制機制也不同。
9. TTL是什麼?有什麼用處,一般那些工具會用到它?ping? traceroute? ifconfig? netstat?
TTL是Time To Live,每通過一個路由就會被減去一,若是它變成0,包會被丟掉。它的主要目的是防止包在有迴路的網絡上死轉,浪費網絡資源。ping和traceroute用到它。
10.linux的五種IO模式/異步模式.
1)同步阻塞I/O
2)同步非阻塞I/O
3)同步I/O複用模型
4) 同步信號驅動I/O
5) 異步I/O模型
11. 請說出http協議的優缺點.
1.支持客戶/服務器模式。2.簡單快速:客戶向服務器請求服務時,只需傳送請求方法和路徑,通訊速度很快。3.靈活:HTTP容許傳輸任意類型的數據對象。4.無鏈接:無鏈接的含義是限制每次鏈接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開鏈接。採用這種方式能夠節省傳輸時間。5.無狀態:HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺乏狀態意味着若是後續處理須要前面的信息,則它必須重傳,致使每次鏈接傳送的數據量增大。缺點就是不夠安全,能夠使用https完成使用
12.NAT類型,UDP穿透原理。
1)Full cone NAT (全克隆nat):一對一NAT一旦一個內部地址(iAddr:port1)映射到外部地址(eAddr:port2)。
2)Address-Restricted cone NAT(地址受限克隆nat):任意外部主機(hostAddr:any)都能經過給eAddr:port2發包到達iAddr:port1的前提是:iAddr:port1以前發送過包到hostAddr:any. "any"也就是說端口不受限制
3). Port-Restricted cone NAT:內部地址(iAddr:port1)映射到外部地址(eAddr:port2),全部發自iAddr:port1的包都經eAddr:port2向外發送。一個外部主機(hostAddr:port3)可以發包到達iAddr:port1的前提是:iAddr:port1以前發送過包到hostAddr:port3.
4). Symmetric NAT(對稱NAT):同內部IP與port的請求到一個特定目的地的IP地址和端口,映射到一個獨特的外部來源的IP地址和端口。同一個內部主機發出一個信息包到不一樣的目的端,不一樣的映射使用外部主機收到了一封包從一個內部主機能夠送一封包回來
13.大規模鏈接上來,併發模型怎麼設計
Epoll+線程池(epoll能夠採用libevent處理)
14.tcp三次握手的,accept發生在三次握手哪一個階段?
三次握手:C----->SYN K
S------>ACK K+1 SYN J
C------->ACK J+1
DONE!
client 的 connect 引發3次握手
server 在socket, bind, listen後,阻塞在accept,三次握手完成後,accept返回一個fd,
16.流量控制與擁塞控制的區別,節點計算機怎樣感知網絡擁塞了?
擁塞控制是把總體當作一個處理對象的,流量控制是對單個的節點。
感知的手段應該很多,好比在TCP協議裏,TCP報文的重傳自己就能夠做爲擁塞的依據。依據這樣的原理, 應該能夠設計出不少手段。
5、算法和數據結構
1.給定一個單向鏈表(長度未知),請設計一個既節省時間又節省空間的算法來找出該鏈表中的倒數第m個元素。實現這個算法,併爲可能出現的特例狀況安排好處理措施。「倒數第m個元素」是這樣規定的:當m=0時,鏈表的最後一個元素將被返回。
解決問題方法思路以下:
方法1、若是咱們知道鏈表的長度n,查找倒數第m個元素,也就是查找正序的第(n - m)個元素(這裏的序號只是爲了分析,可能跟題目不必定正確的符合)。那麼這樣來講就簡單不少。首先遍歷鏈表獲得鏈表長度,而後從新遍歷一次,查找正數第(n-m)個元素。時間複雜度大約是O(2n)。
方法2、咱們是否是能夠提供一個輔助存儲空間,是的咱們在遍歷到鏈表結束的時候能夠回溯到倒數第m個元素。好比用一個支持隨機訪問的容器記錄鏈表每個節點的地址。那麼這樣的就能夠只遍歷一次鏈表就能獲得結果。時間複雜度大約是O(n),可是咱們是用空間換取時間的,輔助存儲空間的大小由m決定,若是m過大也是不可取的。
方法3、頭結點指針爲當前指針,尾節點指針爲拖後指針。開始的時候當前指針和拖後指針初始化爲鏈表的頭結點,首先咱們讓當前指針遍歷到第m個元素,拖後指針不變;而後同步更新當前指針和拖後指針;直到當前指針爲鏈表結尾。這樣咱們就能保證當前指針和拖尾指針之間的距離是m。
代碼以下:
Node* FindMToLastNode(Node* pHead, int m) {
// 查找到第m個元素
Node* pCurrent = pHead;
for (int i = 0; i < m; ++i)
{
if (pCurrent)
{
pCurrent = pCurrent->next;
}
else
{
return NULL;
}
}
Node* pFind = pHead;
while (pCurrent) {
pFind = pFind->next;
pCurrent = pCurrent->next;
}
return pFind;
}
2. 給定一個單向鏈表(長度未知),請遍歷一次就找到中間的指針,假設該鏈表存儲在只讀存儲器,不能被修改
設置兩個指針,一個每次移動兩個位置,一個每次移動一個位置,當第一個指針到達尾節點時,第二個指針就達到了中間節點的位置
處理鏈表問題時,」快行指針「是一種很常見的技巧,快行指針指的是同時用兩個指針來迭代訪問鏈表,只不過其中一個比另外一個超前一些。快指針每每先行幾步,或與慢指針相差固定的步數。
node *create() {
node *p1, *p2, *head;
int cycle = 1, x;
head = (node*)malloc(sizeof(node));
p1 = head;
while (cycle)
{
cout << "please input an integer: ";
cin >> x;
if (x != 0)
{
p2 = (node*)malloc(sizeof(node));
p2->data = x;
p1->next = p2;
p1 = p2;
}
else
{
cycle = 0;
}
}
head = head->next;
p1->next = NULL;
return head;
}
void findmid(node* head) {
node *p1, *p2, *mid;
p1 = head;
p2 = head;
while (p1->next->next != NULL)
{
p1 = p1->next->next;
p2 = p2->next;
mid = p2;
}
}
3. 將一個數組生成二叉排序樹
排序,選數組中間的一個元素做爲根節點,左邊的元素構造左子樹,右邊的節點構造有子樹。
4. 查找數組中第k大的數字?
由於快排每次將數組劃分爲兩組加一個樞紐元素,每一趟劃分你只須要將k與樞紐元素的下標進行比較,若是比樞紐元素下標大就從右邊的子數組中找,若是比樞紐元素下標小從左邊的子數組中找,若是同樣則就是樞紐元素,找到,若是須要從左邊或者右邊的子數組中再查找的話,只須要遞歸一邊查找便可,無需像快排同樣兩邊都須要遞歸,因此複雜度必然下降。
最差狀況以下:假設快排每次都平均劃分,可是都不在樞紐元素上找到第k大第一趟快排沒找到,時間複雜度爲O(n),第二趟也沒找到,時間複雜度爲O(n/2),第k趟找到,時間複雜度爲O(n/2k),因此總的時間複雜度爲O(n(1+1/2+....+1/2k))=O(n),明顯比冒泡快,雖然遞歸深度是同樣的,可是每一趟時間複雜度下降。
5. 紅黑樹的定義和解釋?B樹的基本性質?
紅黑樹:
性質1. 節點是紅色或黑色。
性質2. 根節點是黑色。
性質3. 每一個葉子結點都帶有兩個空的黑色結點(被稱爲黑哨兵),若是一個結點n的只有一個左孩子,那麼n的右孩子是一個黑哨兵;若是結點n只有一個右孩子,那麼n的左孩子是一個黑哨兵。
性質4 每一個紅色節點的兩個子節點都是黑色。(從每一個葉子到根的全部路徑上不能有兩個連續的紅色節點)
性質5. 從任一節點到其每一個葉子的全部路徑都包含相同數目的黑色節點。
B樹:
1.全部非葉子結點至多擁有兩個兒子(Left和Right);
2.全部結點存儲一個關鍵字;
3.非葉子結點的左指針指向小於其關鍵字的子樹,右指針指向大於其關鍵字的子樹;
6. 常見的加密算法?
對稱式加密就是加密和解密使用同一個密鑰。
非對稱式加密就是加密和解密所使用的不是同一個密鑰,一般有兩個密鑰,稱爲「公鑰」和「私鑰」,它們兩個必需配對使用。
DES:對稱算法,數據加密標準,速度較快,適用於加密大量數據的場合;
MD5的典型應用是對一段Message產生fingerprint(指紋),以防止被「篡改」。
RSA是第一個既能用於數據加密也能用於數字簽名的算法。
7. https?
HTTP下加入SSL層,HTTPS的安全基礎是SSL。
8.有一個IP庫,給你一個IP,如何可以快速的從中查找到對應的IP段?不用數據庫如何實現?要求省空間
9.簡述一致性hash算法。
1)首先求memcached服務器(節點)的哈希值,並將其配置到0~232的圓(continuum)。
2)而後採用一樣的方法求出存儲數據的鍵的哈希值,並映射到相同的圓上。
3)而後從數據映射到的位置開始順時針查找,將數據保存到找到的第一個服務器上。若是超過232仍然找不到服務器,就會保存到第一臺memcached服務器上。
11.描述一種hash table的實現方法
1) 除法散列法: p ,令 h(k ) = k mod p ,這裏, p 若是選取的是比較大的素數,效果比較好。並且此法很是容易實現,所以是最經常使用的方法。最直觀的一種,上圖使用的就是這種散列法,公式: index = value % 16,求模數實際上是經過一個除法運算獲得的。
2) 平方散列法 :求index頻繁的操做,而乘法的運算要比除法來得省時。公式: index = (value * value) >> 28 (右移,除以2^28。記法:左移變大,是乘。右移變小,是除)
3) 數字選擇法:若是關鍵字的位數比較多,超過長整型範圍而沒法直接運算,能夠選擇其中數字分佈比較均勻的若干位,所組成的新的值做爲關鍵字或者直接做爲函數值。
4) 斐波那契(Fibonacci)散列法:平方散列法的缺點是顯而易見的,經過找到一個理想的乘數index = (value * 2654435769) >> 28
衝突處理:令數組元素個數爲 S ,則當 h(k) 已經存儲了元素的時候,依次探查 (h(k)+i) mod S , i=1,2,3…… ,直到找到空的存儲單元爲止(或者從頭至尾掃描一圈仍未發現空單元,這就是哈希表已經滿了,發生了錯誤。固然這是能夠經過擴大數組範圍避免的)。
12、各種樹結構的實現和應用
13、hash,任何一個技術面試官必問(例如爲何通常hashtable的桶數會取一個素數?如何有效避免hash結果值的碰撞)
不選素數的話可能會形成hash出值的範圍和原定義的不一致
14.什麼是平衡二叉樹?
左右子樹都是平衡二叉樹,並且左右子樹的深度差值的約對值不大於1。
15.數組和鏈表的優缺點
數組,在內存上給出了連續的空間。鏈表,內存地址上能夠是不連續的,每一個鏈表的節點包括原來的內存和下一個節點的信息(單向的一個,雙向鏈表的話,會有兩個)。
數組優於鏈表的:
A. 內存空間佔用的少。
B. 數組內的數據可隨機訪問,但鏈表不具有隨機訪問性。
C. 查找速度快
鏈表優於數組的:
A. 插入與刪除的操做方便。
B. 內存地址的利用率方面鏈表好。
C. 方便內存地址擴展。
17.最小堆插入,刪除編程實現
18. 4G的long型整數中找到一個最大的,如何作?
每次從磁盤上儘可能多讀一些數到內存區,而後處理完以後再讀入一批。減小IO次數,天然可以提升效率。分批讀入選取最大數,再對緩存的最大數進行快排。
19. 有千萬個string在內存怎麼高速查找,插入和刪除?
對千萬個string作hash,能夠實現高速查找,找到了,插入和刪除就很方便了。關鍵是如何作hash,對string作hash,要減小碰撞頻率。
20.100億個數,求最大的1萬個數,並說出算法的時間複雜度
在內存中維護一個大小爲10000的最小堆,每次從文件讀一個數,與最小堆的堆頂元素比較,若比堆頂元素大,則替換掉堆頂元素,而後調整堆。最後剩下的堆內元素即爲最大的1萬個數,算法複雜度爲O(NlogN)
21.設計一個洗牌的算法,並說出算法的時間複雜度。
(1)全局洗牌法
a)首先生成一個數組,大小爲54,初始化爲1~54
b)按照索引1到54,逐步對每一張索引牌進行洗牌,首先生成一個餘數 value = rand %54,那麼咱們的索引牌就和這個餘數牌進行交換處理
c)等多索引到54結束後,一副牌就洗好了
(2)局部洗牌法:索引牌從1開始,到54結束。這一次索引牌只和剩下尚未洗的牌進行交換, value = index + rand() %(54 - index)
算法複雜度是O(n)
22.請分別用遞歸和非遞歸方法,先序遍歷二叉樹
http://blog.csdn.net/pi9nc/article/details/13008511
24.其餘各類排序方法
http://blog.csdn.net/hguisu/article/details/7776068
25.哈希表衝突解決方法?
常見的hash算法以下:
- 1. 數字分析法 2.平方取中法 3.分段疊加法4.除留餘數法 5.僞隨機法
解決衝突的方法:
- 1. 開放地址法
也叫散列法,主要思想是當出現衝突的時候,以關鍵字的結果值做爲key值輸入,再進行處理,依次直到衝突解決
線性地址再散列法
當衝突發生時,找到一個空的單元或者全表
二次探測再散列
衝突發生時,在表的左右兩側作跳躍式的探測
僞隨機探測再散列
- 2. 再哈希法
同時構造不一樣的哈希函數
- 3. 鏈地址法
將一樣的哈希地址構形成一個同義詞的鏈表
- 4. 創建公共溢出區
創建一個基本表和溢出區,凡是和基本元素髮生衝突都填入溢出區
6、系統架構
1.設計一個服務,提供遞增的SessionID服務,要求保證服務的高可靠性,有哪些方案?集中式/非集中式/分佈式
2.多臺服務器要執行計劃任務,但只有拿到鎖的任務才能執行,有一箇中心服務器來負責分配鎖,但要保證服務的高可靠性。
3.如何有效的判斷服務器是否存活?服務器是否踢出集羣的決策如何產生?
4.兩個服務器如何在同一時刻獲取同一數據的時候保證只有一個服務器能訪問到數據?
能夠採用隊列進行處理,寫一個隊列接口保證同一時間只有一個進程可以訪問到數據,或者對於存取數據庫的來講,數據庫也是能夠加鎖處理的
性能對服務器程序來講是相當重要的了,畢竟每一個客戶都指望本身的請求可以快速的獲得響應並處理。那麼影響服務器性能的首要因素應該是:
(1)系統的硬件資源,好比說CPU個數,速度,內存大小等。不過因爲硬件技術的飛速發展,現代服務器都不缺少硬件資源。所以,須要考慮的主要問題是如何從「軟環境」來提高服務器的性能。
服務器的」軟環境「
(2)一方面是指系統的軟件資源,好比操做系統容許用戶打開的最大文件描述符數量
(3)另外一方面指的就是服務器程序自己,即如何從編程的角度來確保服務器的性能。
主要就要考慮大量併發的處理這涉及到使用進程池或線程池實現高效的併發模式(半同步/半異步和領導者/追隨者模式),以及高效的邏輯處理方式--有限狀態機內存的規劃使用好比使用內存池,以空間換時間,被事先建立好,避免動態分配,減小了服務器對內核的訪問頻率,數據的複製,服務器程序還應該避免沒必要要的數據複製,尤爲是當數據複製發生在用戶空間和內核空間之間時。若是內核能夠直接處理從socket或者文件讀入的數據,則應用程序就不必將這些數據從內核緩衝區拷貝到應用程序緩衝區中。這裏所謂的「直接處理」,是指應用程序不關心這些數據的具體內容是什麼,不須要對它們做任何分析。好比說ftp服務器,當客戶請求一個文件時,服務器只須要檢測目標文件是否存在,以及是否有權限讀取就能夠了,不須要知道這個文件的具體內容,這樣的話ftp服務器就不須要把目標文件讀入應用程序緩衝區而後調用send函數來發送,而是直接使用「零拷貝」函數sendfile直接將其發送給客戶端。另外,用戶代碼空間的數據賦值也應該儘量的避免複製。當兩個工做進程之間須要傳遞大量的數據時,咱們就應該考慮使用共享內存來在他們直接直接共享這些數據,而不是使用管道或者消息隊列來傳遞。上下文切換和鎖:併發程序必須考慮上下文的切換問題,即進程切換或線程切換所致使的系統開銷。即時I/O密集型服務器也不該該使用過多的工做線程(或工做進程),不然進程間切換將佔用大量的CPU時間,服務器真正處理業務邏輯的CPU時間比重就降低了。所以爲每一個客戶鏈接都建立一個工做線程是不可取的。應該使用某種高效的併發模式。(半同步半異步或者說領導者追隨者模式)另外一個問題就是共享資源的加鎖保護。鎖一般被認爲是致使服務器效率低下的一個因素,由於由他引入的代碼不只不處理業務邏輯,並且須要訪問內核資源,所以若是服務器有更好的解決方案,應該儘可能避免使用鎖。或者說服務器必定非要使用鎖的話,儘可能使用細粒度的鎖,好比讀寫鎖,當工做線程都只讀一塊內存區域時,讀寫鎖不會增長系統開銷,而只有當須要寫時才真正須要鎖住這塊內存區域。對於高峯和低峯的伸縮處理,適度的緩存。
6. QQ飛車新用戶註冊時,如何判斷新註冊名字是否已存在?(數量級:幾億)
能夠試下先將用戶名經過編碼方式轉換,如轉換64位整型。而後設置N個區間,每一個區間爲2^64/N的大小。對於新的用戶名,先經過2分尋找該用戶名屬於哪一個區間,而後在在這個區間,作一個hash。對於不一樣的時間複雜度和內存要求能夠設置不一樣N的大小~
加一些基礎的技術面試以外的職業素養的面試問題
1.你在工做中犯了個錯誤,有同事打你小報告,你如何處理?
a.同事之間應該培養和造成良好的同事關係,就是要互相支持而不是互相拆臺,互相學習,互相幫助,共同進步。
b.若是小報告裏邊的事情都是事實也就是說確實是本人作的很差不對的方面,那麼本身應該有則改之,提升本身。若是小報告裏邊的事
情所有不是事實,就是說確實誣告,那麼應該首先堅持日久見人心的態度,鍥而不捨的把本職工做作好,而後在必要的時候經過適當的
方式和領導溝通,相信領導會知道的。
2.你和同事合做完成一個任務,結果任務錯過了截止日期,你如何處理?
3.職業規劃?
4.離職緣由?
5. 項目中遇到的難題,你是如何解決的?
A.時間 b要求 c.方法