[置顶] C/C++超级大火锅
寫在前面
最近接觸到一些基礎(chǔ)知識,平時遇到的編程困惑也加入其中。準(zhǔn)確說是寫給自己看的,但是如果大家可以借鑒就更好。多數(shù)是c/c++,也有少量Java基礎(chǔ)和其他知識,貌似應(yīng)該叫《計算機基礎(chǔ)問題匯總》比較好。不斷更新~~
一、new 跟 malloc 的區(qū)別是什么?
1.malloc/free是C/C++語言的標(biāo)準(zhǔn)庫函數(shù),new/delete是C++的運算符
2.new能夠自動分配空間大小
3.對于用戶自定義的對象而言,用maloc/free無法滿足動態(tài)管理對象的要求。對象在創(chuàng)建的同時要自動執(zhí)行構(gòu)造函數(shù),對象在消亡之前要自動執(zhí)行析構(gòu)函數(shù)。由于malloc/free是庫函數(shù)而不是運算符,不在編譯器控制權(quán)限之內(nèi),不能夠把執(zhí)行構(gòu)造函數(shù)和析構(gòu)函數(shù)的任務(wù)強加于malloc/free。因此C++需要一個能對對象完成動態(tài)內(nèi)存分配和初始化工作的運算符new,以及一個能對對象完成清理與釋放內(nèi)存工作的運算符delete—簡而言之 new/delete能進行對對象進行構(gòu)造和析構(gòu)函數(shù)的調(diào)用進而對內(nèi)存進行更加詳細(xì)的工作,而malloc/free不能。
new 是一個操作符,可以重載
malloc 是一個函數(shù),可以覆蓋
new 初始化對象,調(diào)用對象的構(gòu)造函數(shù),對應(yīng)的delete調(diào)用相應(yīng)的析構(gòu)函數(shù)
malloc 僅僅分配內(nèi)存,free僅僅回收內(nèi)存
二、寫時拷貝
有一定經(jīng)驗的程序員應(yīng)該都知道Copy On Write(寫時復(fù)制)使用了“引用計數(shù)”,會有一個變量用于保存引用的數(shù)量。當(dāng)?shù)谝粋€類構(gòu)造時,string的構(gòu)造函數(shù)會根據(jù)傳入的參數(shù)從堆上分配內(nèi)存,當(dāng)有其它類需要這塊內(nèi)存時,這個計數(shù)為自動累加,當(dāng)有類析構(gòu)時,這個計數(shù)會減一,直到最后一個類析構(gòu)時,此時的引用計數(shù)為1或是0,此時,程序才會真正的Free這塊從堆上分配的內(nèi)存。
引用計數(shù)就是string類中寫時才拷貝的原理!
什么情況下觸發(fā)Copy On Write(寫時復(fù)制)
很顯然,當(dāng)然是在共享同一塊內(nèi)存的類發(fā)生內(nèi)容改變時,才會發(fā)生Copy On Write(寫時復(fù)制)。比如string類的[]、=、+=、+等,還有一些string類中諸如insert、replace、append等成員函數(shù)等,包括類的析構(gòu)時。
三、使用引用應(yīng)該注意什么?
《C++高級進階教程》中指出,引用的底層實現(xiàn)由指針按照指針常量的方式實現(xiàn),見:C++引用的本質(zhì)。非要說區(qū)別,那么只能是使用上存在的區(qū)別。
引用就是對某個變量其別名。對引用的操作與對應(yīng)變量的操作的效果完全一樣。
申明一個引用的時候,切記要對其進行初始化。 引用聲明完畢后,相當(dāng)于目標(biāo)變量名有兩個名稱,即該目標(biāo)原名稱和引用名,不能再把該引用名作為其他變量名的別名。聲明一個引用,不是新定義了一個變量,它只 表示該引用名是目標(biāo)變量名的一個別名,它本身不是一種數(shù)據(jù)類型,因此引用本身不占存儲單元,系統(tǒng)也 不給引用分配存儲單元。
不能建立數(shù)組的引用。 // 數(shù)組的元素不能是引用
定義引用的類型,是編譯時確定的, int a = 6; double &b = a;
詳見:http://blog.csdn.net/scythe666/article/details/50976449
四、如何和一個函數(shù)共享一塊內(nèi)存地址
如果不用修改指針,只要傳指針過去,那邊用指針接收,如果需要修改指針。
指針的引用方法如下:
#include <stdio.h>void allocatmemory(float *&data) {data=new float[100]; }int main() {float *data = NULL;allocatmemory(data);if(!data){printf("data is null\r\n");}else{printf("data has been allocated memory.\r\n");delete []data;data = NULL;} }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
還有一種方法是利用指針的指針。
五、c++中多態(tài)體現(xiàn)
編譯時多態(tài):編譯期確定,重載,模板元編程
運行時多態(tài):虛函數(shù),晚綁定
六、虛函數(shù)表存放的位置,一個類有多少個虛函數(shù)表?
詳見:http://blog.csdn.net/scythe666/article/details/50977593
虛函數(shù)表的指針存在于對象實例中最前面的位置。虛函數(shù)表是類所擁有的,程序運行過程中不能夠修改,它存放在常量區(qū)。
一個類若繼承了多個含有虛函數(shù)的基類,那么該類就有對應(yīng)數(shù)量的虛函數(shù)表。
七、vector、deque、list、set、map區(qū)別
1、vector
相當(dāng)于數(shù)組。在內(nèi)存中分配一塊連續(xù)的內(nèi)存空間進行存儲。支持不指定vector大小的存儲。STL內(nèi)部實現(xiàn)時,首先分配一個非常大的內(nèi)存空間預(yù)備進行存儲,即capacituy()函數(shù)返回的大小,當(dāng)超過此分配的空間時再整體重新放分配一塊內(nèi)存存儲,這給人以vector可以不指定vector即一個連續(xù)內(nèi)存的大小的感覺。通常此默認(rèn)的內(nèi)存分配能完成大部分情況下的存儲。
優(yōu)點:
(1) 不指定一塊內(nèi)存大小的數(shù)組的連續(xù)存儲,即可以像數(shù)組一樣操作,但可以對此數(shù)組進行動態(tài)操作。通常體現(xiàn)在push_back() pop_back()
(2) 隨機訪問方便,即支持[ ]操作符和vector.at()
(3) 節(jié)省空間。
缺點:
(1) 在內(nèi)部進行插入刪除操作效率低。
(2)只能在vector的最后進行push和pop,不能在vector的頭進行push和pop。
(3) 當(dāng)動態(tài)添加的數(shù)據(jù)超過vector默認(rèn)分配的大小時要進行整體的重新分配、拷貝與釋放
2、list
雙向鏈表。每一個結(jié)點都包括一個信息塊Info、一個前驅(qū)指針Pre、一個后驅(qū)指針Post。可以不分配必須的內(nèi)存大小方便的進行添加和刪除操作。使用的是非連續(xù)的內(nèi)存空間進行存儲。
優(yōu)點:
(1) 不使用連續(xù)內(nèi)存完成動態(tài)操作。
(2) 在內(nèi)部方便的進行插入和刪除操作
(3) 可在兩端進行push、pop
缺點:
(1)不能進行內(nèi)部的隨機訪問,即不支持[]操作符和vector.at()
>
(2)相對于verctor占用內(nèi)存多
3、deque
雙端隊列 double-end queue
deque是在功能上合并了vector和list。
優(yōu)點:
(1) 隨機訪問方便,即支持[ ]操作符和vector.at()
(2) 在內(nèi)部方便的進行插入和刪除操作
(3) 可在兩端進行push、pop
缺點:
(1) 占用內(nèi)存多
使用區(qū)別:
(1)如果你需要高效的隨即存取,而不在乎插入和刪除的效率,使用vector
(2)如果你需要大量的插入和刪除,而不關(guān)心隨即存取,則應(yīng)使用list
(3)如果你需要隨即存取,而且關(guān)心兩端數(shù)據(jù)的插入和刪除,則應(yīng)使用deque
4、map是,key-value對集合
5、set,就是key=value的map
八、線程、進程、協(xié)程區(qū)別和聯(lián)系
力薦插圖神作:http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html
1、進程是一個具有獨立功能的程序關(guān)于某個數(shù)據(jù)集合的一次運行活動。它可以申請和擁有系統(tǒng)資源,是一個動態(tài)的概念,是一個活動的實體。它不只是程序的代碼,還包括當(dāng)前的活動,通過程序計數(shù)器的值和處理寄存器的內(nèi)容來表示。
進程是一個“執(zhí)行中的程序”。程序是一個沒有生命的實體,只有處理器賦予程序生命時,它才能成為一個活動的實體,我們稱其為進程。
2、通常在一個進程中可以包含若干個線程,它們可以利用進程所擁有的資源。在引入線程的操作系統(tǒng)中,通常都是把進程作為分配資源的基本單位,而把線程作為獨立運行和獨立調(diào)度的基本單位。由于線程比進程更小,基本上不擁有系統(tǒng)資源,只擁有一點在運行中必不可少的資源(如程序計數(shù)器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。故對它的調(diào)度所付出的開銷就會小得多,能更高效的提高系統(tǒng)內(nèi)多個程序間并發(fā)執(zhí)行的程度。
3、線程和進程的區(qū)別在于,子進程和父進程有不同的代碼和數(shù)據(jù)空間,而多個線程則共享數(shù)據(jù)空間,每個線程有自己的執(zhí)行堆棧和程序計數(shù)器為其執(zhí)行上下文。多線程主要是為了節(jié)約CPU時間,發(fā)揮利用,根據(jù)具體情況而定。線程的運行中需要使用計算機的內(nèi)存資源和CPU。
4、線程與進程的區(qū)別歸納:
a.地址空間和其它資源:進程間相互獨立,同一進程的各線程間共享。某進程內(nèi)的線程在其它進程不可見。
b.通信:進程間通信IPC,線程間可以直接讀寫進程數(shù)據(jù)段(如全局變量)來進行通信——需要進程同步和互斥手段的輔助,以保證數(shù)據(jù)的一致性。
c.調(diào)度和切換:線程上下文切換比進程上下文切換要快得多。
d.在多線程OS中,進程不是一個可執(zhí)行的實體。
5、優(yōu)缺點
線程和進程在使用上各有優(yōu)缺點:線程執(zhí)行開銷小,但不利于資源的管理和保護;而進程正相反。同時,線程適合于在SMP機器上運行,而進程則可以跨機器遷移。
協(xié)程詳見:http://blog.csdn.net/scythe666/article/details/50987055
九、MySQL innodb myisam 區(qū)別
十、堆空間、棧空間區(qū)別
十一、64位程序指針多大
8字節(jié)
十二、函數(shù)內(nèi)、函數(shù)外 static 區(qū)別
十三、TCP三次握手和四次揮手
詳見http://blog.csdn.net/scythe666/article/details/50967632
十四、守護進程和服務(wù)
十五、mysql跑了很久都沒有出結(jié)果,排除邏輯問題,請問如何檢測?
十六、session有什么缺點
十七、數(shù)據(jù)庫范式
十八、java反射機制
十九、java內(nèi)存回收,是否可以強制內(nèi)存回收
二十、c++有沒有內(nèi)存回收?
二十一、函數(shù)入口地址
如果你在調(diào)試程序時查看程序的匯編碼,可以發(fā)現(xiàn),調(diào)用函數(shù)的語句對應(yīng)的匯編碼是
jmp 函數(shù)名(入口地址)- 1
- 1
這樣的形式,函數(shù)在內(nèi)存中的存在形式就是一段代碼而已,入口地址即函數(shù)代碼段在內(nèi)存中的首地址。
二十二、如何判斷一個數(shù)是2的整數(shù)次方
我的分析過程是:
這個肯定是從二進制入手,因為計算機是二進制處理,而且最關(guān)鍵的是2的整數(shù)次方是很特殊的 –> 二進制中只有一個1。所以我想可以用二進制來判斷1的個數(shù),寫函數(shù)
x = x&(x-1); //每次減少一個1 cnt++;- 1
- 2
- 1
- 2
如果cnt為1,這是2的整數(shù)次方。這樣的方法還不是最簡單的方法,可以直接看 x&(x-1) 是否為0即可。
二十三、兩個數(shù),如何判斷其中一個數(shù)的二進制需要變換多少位才能到另一個數(shù)
分析:這題比較直接,很明顯也是到二進制來處理,相同位放過,不同就標(biāo)識為1,這樣自然聯(lián)想到異或操作,這就是異或操作的定義。
二十四、給10^8數(shù)量級的數(shù)組,其他數(shù)都是偶數(shù)次,只有一個數(shù)出現(xiàn)一次。如何快速找到這個數(shù)?
將所有數(shù)異或起來,結(jié)果就是那個數(shù)。
二十五、10個文件,每個文件1G,如何排序?
分桶,桶與桶之間一定有大小關(guān)系,比如按照大小分100個桶,每個文件遍歷,分到桶里,桶內(nèi)排序,然后合起來就好。
二十六、c++ volatile
volatile 的意思是,“這個數(shù)據(jù)不知何時會被改變”,可能當(dāng)時環(huán)境正在被改變(可能有多任務(wù)、多線程或者中斷處理),所以 volatile 告訴編譯器不要擅自做出有關(guān)該數(shù)據(jù)的任何假定,優(yōu)化期間尤其如此。
volatile 的確切含義與具體機器相關(guān),可以通過閱讀編譯器文檔來理解,使用volatile的程序在移到新的機器或編譯器時通常必須改變。
與 const 限定符相同的,volatile 也是類型限定符。
與 const 類似的是,只能將 volatile 對象的地址賦給指向 volatile 的指針,或者將指向 volatile 類型的 volatile 類型的指針復(fù)制給指向 volatile 的指針。只有當(dāng)引用為 volatile 時,才可以使用 volatile 對象對引用進行初始化。
二十七、內(nèi)存對齊
以最大單位大小對齊,比如一個 struct 中有 int[4]; 和 char
詳見:http://blog.csdn.net/scythe666/article/details/50985461
二十八、lib和dll的區(qū)別
obj:目標(biāo)文件(二進制)、通過鏈接器生成exe,obj給出程序的相對地址;exe給出絕對地址。
lib(編譯時):若干obj集合。
dll(運行時):可實際執(zhí)行的二進制代碼,包含可由多個程序同時使用的代碼和數(shù)據(jù)的庫。
不是可執(zhí)行文件,若知道dll函數(shù)原型,程序中LoadLibrary載入,getProAddress();
靜態(tài)鏈接使用靜態(tài)鏈接庫:鏈接庫從靜態(tài)lib獲取引用函數(shù),應(yīng)用程序較大。
動態(tài)鏈接:lib包含被dll導(dǎo)出的函數(shù)名和位置,dll包含實際函數(shù)和數(shù)據(jù),應(yīng)用程序用lib文件鏈接到dll文件。
區(qū)別總結(jié):
.dll用于運行階段,如調(diào)用SetWindowText()函數(shù)等,需要在user32.dll中找到該函數(shù)。DLL可以簡單認(rèn)為是一種包含供別人調(diào)用的函數(shù)和資源的可執(zhí)行文件。
.lib用于鏈接階段,在鏈接各部分目標(biāo)文件(通常為.obj)到可執(zhí)行文件(通常為.exe)過程中,需要在.lib文件中查找動態(tài)調(diào)用函數(shù)(一般為DLL中的函數(shù))的地址信息,此時需要在lib文件中查找,如查找SetWindowText()函數(shù)的地址偏移就需要查找user32.lib文件。(.lib也可用于靜態(tài)鏈接的內(nèi)嵌代碼)
二十九、如何看到系統(tǒng)端口占用情況?這個方法底層實現(xiàn)是怎樣的?
三十、C++中的驟死式
如果 || 或者 && 如果判斷前面不符合要求,后面不會進行計算
三十一、常見網(wǎng)絡(luò)錯誤代碼
三十二、c++運算符優(yōu)先級
三十三、c++運算符重載
《劍指offer》上賦值運算符重載
class whString { public:whString& operator = (const whString &str);whString () {m_pData = NULL;};whString(const char *s) {m_pData = new char[strlen(s) + 1];strcpy(m_pData,s);}~whString() {delete []m_pData;m_pData = NULL;}void stringPrint() {cout<<m_pData<<endl;} private:char *m_pData; };whString& whString::operator = (const whString &str) {cout<<"enter whString::operator =()"<<endl;if( &str == this ) //傳入的是否是同一個實例return *this;delete []m_pData;m_pData = NULL;m_pData = new char[strlen(str.m_pData) + 1];strcpy(m_pData,str.m_pData);return *this; }int main() {//freopen("input.txt","r",stdin);whString a = "abc";whString b;b = a;b.stringPrint();return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
注意點:
1、是否把返回值類型聲明為該類型引用,并返回實例自身引用(*this),這樣才允許連續(xù)賦值。
2、傳入?yún)?shù)聲明為const。
3、是否釋放自身內(nèi)存。
4、檢測是否傳入同一個參數(shù)。
如上代碼有更好的形式:
whString& whString::operator = (const whString &str) {cout<<"enter whString::operator =()"<<endl;if( &str != this ) {whString strTmp(str);char* pTmp = strTmp.m_pData;strTmp.m_pData = m_pData; //自動調(diào)用析構(gòu)m_pData = pTmp;}return *this; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
避免了異常安全性。
三十四、c++ 模板元編程
詳見:http://blog.csdn.net/scythe666/article/details/50899345
三十五、環(huán)境變量作用
三十六、包含目錄 庫目錄 附加依賴項【編譯器 參數(shù)詳解】
三十七、GPL LGPL 等開源協(xié)議小覽
三十八、常說的sdk、ide、jdk、jre、java runtime 區(qū)別
http://www.2cto.com/kf/201212/178270.html
SDK(Software Develop Kit,軟件開發(fā)工具包),用于幫助開發(fā)人員的提高工作效率。各種不同類型的軟件開發(fā),都可以有自己的SDK。Windows有Windows SDK,DirectX 有 DirectX 9 SDK,.NET開發(fā)也有Microsoft .NET Framework SDK。JAVA開發(fā)也不含糊,也有自己的Java SDK。
java SDK最早叫Java Software Develop Kit,后來改名為JDK,即Java Develop Kit。
JDK作為Java開發(fā)工具包,主要用于構(gòu)建在Java平臺上運行的應(yīng)用程序、Applet 和組件等。
JRE(Java Runtime Environment,Java運行環(huán)境),也就是Java平臺。所有的Java程序都要在JRE下才能運行。JDK的工具也是Java程序,也需要JRE才能運行。為了保持JDK的獨立性和完整性,在JDK的安裝過程中,JRE也是安裝的一部分。所以,在JDK的安裝目錄下有一個名為jre的目錄,用于存放JRE文件。
JVM(Java Virtual Machine,Java虛擬機)是JRE的一部分。它是一個虛構(gòu)出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現(xiàn)的。JVM有自己完善的硬件架構(gòu),如處理器、堆棧、寄存器等,還具有相應(yīng)的指令系統(tǒng)。Java語言最重要的特點就是跨平臺運行。使用JVM就是為了支持與操作系統(tǒng)無關(guān),實現(xiàn)跨平臺。
下圖清晰地展示了JDK(Java SDK)、JRE和JVM之間的關(guān)系:
三十九、注冊表詳細(xì)理解
四十、c++ 函數(shù)指針 指針函數(shù)
四十一、inline關(guān)鍵字
四十二、Lambda表達(dá)式
四十三、c++ 關(guān)鍵字整理
四十四、類的初始化列表
1、作用
2、什么樣的成員必須在初始化列表中初始化
3、初始化列表執(zhí)行時間
四十五、c++ 函數(shù)簽名
四十六、c++ 基本類型、復(fù)合類型
四十七、qmake 小覽
四十八、c++多重繼承 虛基類
四十九、cpu發(fā)展史
五十、域名解析過程
五十一、ebp esp 等寄存器作用
五十二、c++ 中隱藏、重寫、覆蓋、重載區(qū)別
五十三、委托機制
五十四、extern關(guān)鍵字
五十五、DDos、SYNFLOOD攻擊
五十六、OS 主分區(qū)
五十七、宏函數(shù)
五十八、java類是什么負(fù)責(zé)創(chuàng)建的
五十九、郵件協(xié)議
http://www.tuicool.com/articles/MviQv2
1 郵件收發(fā)過程
電子郵件發(fā)送協(xié)議 是一種基于“ 推 ”的協(xié)議,主要包括 SMTP ; 郵件接收協(xié)議 則是一種基于“ 拉 ”的協(xié)議,主要包括 POP協(xié)議 和 IMAP協(xié)議 ,在正式介紹這些協(xié)議之前,我們先給出郵件收發(fā)的體系結(jié)構(gòu):
從上圖可以看出郵件收發(fā)的整個過程大致如下:
(1)發(fā)件人調(diào)用PC機中的用戶代理編輯要發(fā)送的郵件。
(2)發(fā)件人點擊屏幕上的”發(fā)送郵件“按鈕,把發(fā)送郵件的 工作全部交給用戶代理來完成。用戶代理通過SMTP協(xié)議將郵件發(fā)送給發(fā)送方的郵件服務(wù)器( 在這個過程中,用戶代理充當(dāng)SMTP客戶,而發(fā)送方的郵件服務(wù)器則充當(dāng)SMTP服務(wù)器 )。
(3)發(fā)送方的郵件服務(wù)器收到用戶代理發(fā)來的郵件后,就把收到的郵件臨時存放在郵件緩存隊列中,等待時間成熟的時候再發(fā)送到接收方的郵件服務(wù)器( 等待時間的長短取決于郵件服務(wù)器的處理能力和隊列中待發(fā)送的信件的數(shù)量 )。
(4)若現(xiàn)在時機成熟了,發(fā)送方的郵件服務(wù)器則向接收方的郵件服務(wù)器發(fā)送郵件緩存中的郵件。在發(fā)送郵件之前,發(fā)送方的郵件服務(wù)器的SMTP客戶與接收方的郵件服務(wù)器的SMTP服務(wù)器需要事先建立TCP連接,之后再將隊列中 的郵件發(fā)送出去。 值得注意的是,郵件不會在因特網(wǎng)中的某個中間郵件服務(wù)器落地 。
(5)接收郵件服務(wù)器中的SMTP服務(wù)器進程在收到郵件后,把郵件放入收件人的用戶郵箱中,等待收件人進行讀取。
(6)收件人在打算收信時,就運行PC機中的用戶代理,使用POP3(或IMAP)協(xié)議讀取發(fā)送給自己的郵件。 注意,在這個過程中,收件人是POP3客戶,而接收郵件服務(wù)器則是POP3客戶,箭頭的方向是從郵件服務(wù)器指向接收用戶,因為這是一個“ 拉 ”的操作 。
2 電子郵件協(xié)議
http://server.zzidc.com/fwqfl/312.html
電子郵件在發(fā)送和接收的過程中還要遵循一些基本協(xié)議和標(biāo)準(zhǔn),這些協(xié)議主要有SMTP、POP3、IMAP、MIME等。
(1)SMTP協(xié)議
SMTP(Simple Mail Transfer Protocol,簡單郵件傳輸協(xié)議)是Internet上基于TCP/IP的應(yīng)用層協(xié)議,使用于主機與主機之間的電子郵件交換。SMTP的特點是簡單,它只定義了郵件發(fā)送方和接收方之間的連接傳輸,將電子郵件有一臺計算機傳送到另一臺計算機,而不規(guī)定其他任何操作,如用戶界面的交互、郵件的接收、郵件存儲等。Internet上幾乎所有主機都運行著遵循SMTP的電子郵件軟件,因此使用非常普通。另一方面,SMTP由于簡單,因而有其一定的局限性,它只能傳送ASCII文本文件,而對于一些二進制數(shù)據(jù)文件需要進行編碼后才能傳送。
(2)POP3協(xié)議和IMAP協(xié)議
電子郵件用戶要從郵件服務(wù)器讀取或下載郵件時必須要有郵件讀取協(xié)議。現(xiàn)在常用的郵件讀取協(xié)議有兩個,一個是郵局協(xié)議的第三版本(POP3,Post Office Protocol Version 3),另一個是因特網(wǎng)報文存取協(xié)議(IMAP,Internet Message Access Protocol)。
POP3是一個非常簡單、但功能有限的郵件讀取協(xié)議,大多數(shù)ISP都支持POP3。當(dāng)郵件用戶將郵件接收軟件設(shè)定為POP3閱讀電子郵件時,每當(dāng)使用者要閱讀電子郵件時,它都會把所有信件內(nèi)容下載至使用者的計算機,此外,他可選擇把郵件保留在郵件服務(wù)器上或是不保留郵件在服務(wù)器上。無IMAP是另一種郵件讀取協(xié)議。當(dāng)郵件用戶將郵件接收設(shè)定IMAP閱讀電子郵件時,它并不會把所有郵件內(nèi)容下載至計算機,而只下載郵件的主題等信息。
(3)多途徑Internet郵件擴展協(xié)議
多用途Internet郵件擴展協(xié)議(MIME,Multipose Internet Mail Extensions)是一種編碼標(biāo)準(zhǔn),它解決了SMTP只能傳送ASCII文本的限制。MIME定義了各種類型數(shù)據(jù),如聲音、圖像、表格、二進制數(shù)據(jù)等的編碼格式,通過對這些類型的數(shù)據(jù)編碼并將它們作為郵件中的附件進行處理,以保證這些部分內(nèi)容完整、正確地傳輸。因此,MIME增強了SMTP的傳輸功能,統(tǒng)一了編碼規(guī)范。
六十、c++用struct和class定義類型,有什么區(qū)別
如果沒有標(biāo)注成員函數(shù)或者成員變量的訪問權(quán)限級別,在struct中默認(rèn)的是public,而在class中默認(rèn)的是private。
六十一、map reduce 基本知識
六十二、管程
管程 (英語:Monitors,也稱為監(jiān)視器) 是一種程序結(jié)構(gòu),結(jié)構(gòu)內(nèi)的多個子程序(對象或模塊)形成的多個工作線程互斥訪問共享資源。這些共享資源一般是硬件設(shè)備或一群變量。管程實現(xiàn)了在一個時間點,最多只有一個線程在執(zhí)行管程的某個子程序。與那些通過修改數(shù)據(jù)結(jié)構(gòu)實現(xiàn)互斥訪問的并發(fā)程序設(shè)計相比,管程實現(xiàn)很大程度上簡化了程序設(shè)計。
簡單點說就是只能被單個線程執(zhí)行的代碼了,舉個例子假如一個管程類叫atm(取款機),里面有兩個方法叫提款 取款,不同的人為不同的線程,但是atm只允許一個人在一個時間段中進行操作,也就是單線程,那么這個atm就需要一個鎖,單一個人在使用時,其他的人只能wait。再者一個人如果使用的時間太長也不行,所以需要一個條件變量來約束他
條件變量可以讓一個線程等待時讓另一線程進入管程,這樣可以有效防止死鎖
六十三、子進程可以訪問父進程的變量嗎?
答:
子進程可以訪問父進程變量。子進程“繼承”父進程的數(shù)據(jù)空間,堆和棧,其地址總是一樣的。
因為在fork時整個虛擬地址空間被復(fù)制,但是虛擬地址空間所對應(yīng)的物理內(nèi)存開始卻沒有復(fù)制,如果有寫入時寫時拷貝。比如,這個時候父子進程中變量 x對應(yīng)的虛擬地址和物理地址都相同,但等到虛擬地址空間被寫時,對應(yīng)的物理內(nèi)存空間被復(fù)制,這個時候父子進程中變量x對應(yīng)的虛擬地址還是相同的,但是物理地址不同,這就是”寫時復(fù)制”。還有父進程和子進程是始終共享正文段(代碼段)。
六十四、進程間通信(IPC)的幾種方式?哪種效率最高?
答:
(1)管道PIPE和有名管道FIFO – 比如shell的重定向
(2)信號signal – 比如殺死某些進程kill -9,比如忽略某些進程nohup ,信號是一種軟件中斷
(3)消息隊列 – 相比共享內(nèi)存會慢一些,緩沖區(qū)有限制,但不用加鎖,適合命令等小塊數(shù)據(jù)。
(4)共享內(nèi)存 – 最快的IPC方式,同一塊物理內(nèi)存映射到進程A、B各自的進程地址空間,可以看到對方的數(shù)據(jù)更新,需要注意同步機制,比如互斥鎖、信號量。適合傳輸大量數(shù)據(jù)。
(5)信號量 – PV操作,生產(chǎn)者與消費者示例;
(6)套接字 – socket網(wǎng)絡(luò)編程,網(wǎng)絡(luò)中不同主機間的進程間通信,屬高級進程間通信。
六十五、Linux多線程同步的幾種方式 ?
答:
(1)互斥鎖(mutex) (2)條件變量 (3)信號量。 如同進程一樣,線程也可以通過信號量來實現(xiàn)同步,雖然是輕量級的。
這里要注意一點,互斥量可通過pthread_mutex_setpshared接口設(shè)置可用于進程間同步;條件標(biāo)量在初始化時,也可以通過接口pthread_condattr_setpshared指定該條件變量可用于進程進程間同步。
六十六、多核與多個CPU的區(qū)別?
我們首先來了解下二者:
何為多核CPU?簡單理解就是,我們將多個核心裝載一個封裝里,讓用戶理解成這是一個處理器。這樣好處就是原本運行在單機上跑的程序基本不需要更改就能夠獲得非常不錯的性能。多核心發(fā)展趨勢也是英特爾一直堅持的方式。
何為多個CPU運行呢?了解服務(wù)器的人都知道有單路,雙路,多路之分,而ARM針對服務(wù)器市場推出的處理器也是呈現(xiàn)這種方式,最終能夠形成分布式系統(tǒng),其實跟多核CPU內(nèi)部的分布式結(jié)果是一樣的,只不過那個從外部看是單個處理器。這種方式在軟件支持、運行、故障方面的問題較多。
下面我們舉一個例子來形象的比喻一下:
例如,你需要搬很多磚,你現(xiàn)在有一百只手。當(dāng)你將這一百只手全安裝到一個人身上,這模式就是多核。當(dāng)你將這一百之手安裝到50個人身上工作,這模式就是多CPU。
那么多核跟多CPU在應(yīng)用中有什么區(qū)別呢?首先我們看多核的模式,就是一個人身上安一百個手的方式,這個即使這個人再笨,干活速度也要比只有兩只手的人要快。
但是將一百只手放在一個人身上,同樣會帶來一些問題,例如一百只手搬磚太多了,這樣身體的脊柱就受不了了,就會頂不住。這就是CPU的多核的極限。于是,當(dāng)搬磚數(shù)量較多的時候,多CPU的方式就顯現(xiàn)出來了。人多力量大呀。
六十七、軟中斷和硬中斷的區(qū)別?
答:
從本質(zhì)上來講,中斷是一種電信號,當(dāng)設(shè)備有某種事件發(fā)生時,它就會產(chǎn)生中斷,通過總線把電信號發(fā)送給中斷控制器。
如果中斷的線是激活的,中斷控制器就把電信號發(fā)送給處理器的某個特定引腳。處理器于是立即停止自己正在做的事,
跳到中斷處理程序的入口點,進行中斷處理。
①硬中斷是由外部設(shè)備對CPU的中斷,具有隨機性和突發(fā)性;軟中斷由程序控制,執(zhí)行中斷指令產(chǎn)生的、無外部施加的中斷請求信號,因此不是隨機的而是安排好的。如信號就是軟件中斷,鍵盤輸入、鼠標(biāo)點擊就是硬件中斷。
②硬中斷的中斷響應(yīng)周期,CPU需要發(fā)中斷回合信號(NMI不需要),軟中斷的中斷響應(yīng)周期,CPU不需發(fā)中斷回合信號。
③硬中斷的中斷號是由中斷控制器提供的(NMI硬中斷中斷號系統(tǒng)指定為02H);軟中斷的中斷號由指令直接給出,無需使用中斷控制器。
④硬中斷是可屏蔽的(NMI硬中斷不可屏蔽),軟中斷不可屏蔽。
六十八、cpu指令集
指令集是存儲在CPU內(nèi)部,對CPU運算進行指導(dǎo)和優(yōu)化的硬程序。擁有這些指令集,CPU就可以更高效地運行。Intel有x86,EM64T,MMX,SSE,SSE2,SSE3,SSSE3 (Super SSE3),SSE4.1,SSE4.2,AVX。AMD主要是x86,x86-64,3D-Now!指令集。
六十九、UDP和TCP可以綁定同一個端口?
答:
網(wǎng)絡(luò)中可以被命名和尋址的通信端口,是操作系統(tǒng)可分配的一種資源。
按照OSI七層協(xié)議的描述,傳輸層與網(wǎng)絡(luò)層在功能上的最大區(qū)別是傳輸層(第四層)提供進程通信能力。從這個意義上講,網(wǎng)絡(luò)通信的最終地址就不僅僅是主機地址了,還包括可以描述進程的某種標(biāo)識符。為此,TCP/IP協(xié)議提出了協(xié)議端口(protocol port,簡稱端口)的概念,用于標(biāo)識通信的進程。
端口是一種抽象的軟件結(jié)構(gòu)(包括一些數(shù)據(jù)結(jié)構(gòu)和I/O緩沖區(qū))。應(yīng)用程序(即進程)通過系統(tǒng)調(diào)用與某端口建立連接(binding)后,傳輸層傳給該端口的數(shù)據(jù)都被相應(yīng)進程所接收,相應(yīng)進程發(fā)給傳輸層的數(shù)據(jù)都通過該端口輸出。在TCP/IP協(xié)議的實現(xiàn)中,端口操作類似于一般的I/O操作,進程獲取一個端口,相當(dāng)于獲取本地唯一的I/O文件,可以用一般的讀寫原語訪問之。
也就是說:一個端口只能同時被分配給一個進程
類似于文件描述符,每個端口都擁有一個叫端口號(port number)的整數(shù)型標(biāo)識符,用于區(qū)別不同端口。由于TCP/IP傳輸層的兩個協(xié)議TCP和UDP是完全獨立的兩個軟件模塊,因此各自的端口號也相互獨立,如TCP有一個255號端口,UDP也可以有一個255號端口,二者并不沖突。
端口號的分配是一個重要問題。有兩種基本分配方式:第一種叫全局分配,這是一種集中控制方式,由一個公認(rèn)的中央機構(gòu)根據(jù)用戶需要進行統(tǒng)一分配,并將結(jié)果公布于眾。第二種是本地分配,又稱動態(tài)連接,即進程需要訪問傳輸層服務(wù)時,向本地操作系統(tǒng)提出申請,操作系統(tǒng)返回一個本地唯一的端口號,進程再通過合適的系統(tǒng)調(diào)用將自己與該端口號聯(lián)系起來(綁扎)。TCP/IP端口號的分配中綜合了上述兩種方式。TCP/IP將端口號分為兩部分,少量的作為保留端口,以全局方式分配給服務(wù)進程。因此,每一個標(biāo)準(zhǔn)服務(wù)器都擁有一個全局公認(rèn)的端口(即周知口,well-known port),即使在不同機器上,其端口號也相同。剩余的為自由端口,以本地方式進行分配。TCP和UDP均規(guī)定,小于256的端口號才能作保留端口。
再討論一下,一個服務(wù)器監(jiān)控一個端口,比如80端口,它為什么可以建立上成千上萬的連接?
首先, 一個TCP連接需要由四元組來形成,即(src_ip,src_port,dst_ip,dst_port)。當(dāng)一個連接請求過來的時候,服務(wù)端調(diào)用accept函數(shù),新生成一個socket,這個socket所占用的本地端口依然是80端口。由四元組就很容易分析到了,同一個(src_ip,src_port),它所對應(yīng)的(dst_ip,dst_port)可以無窮變化,這樣就可以建立很多個客戶端的請求了。
七十、如何解決哲學(xué)家進餐問題?
答:
哲學(xué)家進餐問題是由荷蘭學(xué)者Dijkstra提出的經(jīng)典的線程和進程間步問題之一。產(chǎn)生死鎖的四個必要條件是:
(1)互斥條件;
(2)請求和保持條件;
(3)不可剝奪條件;
(4)環(huán)路等待條件。
筷子是絕對互斥的,我們可以破壞其它三種條件來解決哲學(xué)家進餐問題。解決辦法如下:
(1)破壞請求保持條件
利用原子思想完成。即只有拿起兩支筷子的哲學(xué)家才可以進餐,否則,一支筷子也不拿。
(2)破壞不可剝奪條件
當(dāng)哲學(xué)家相互等待時,選擇一個哲學(xué)家作為犧牲者,放棄手中的筷子,這樣就保證有一位哲學(xué)家可以就餐了。
(3)破壞環(huán)路等待條件
解法一:奇數(shù)號哲學(xué)家先拿他左邊的筷子,偶數(shù)號哲學(xué)家先拿他右邊的筷子。這樣破壞了同方向環(huán)路;
解法二:至多允許N-1位哲學(xué)家進餐,將最后一個哲學(xué)家停止申請資源,斷開環(huán)路。最終能保證有一位哲學(xué)家能進餐,用完釋放兩只筷子,從而使更多的哲學(xué)家能夠進餐。
七十一、堆排序和快速排序的區(qū)別?
答:
堆排序和快速排序都是比較類非線性比較類排序中較優(yōu)的排序方法,均是不穩(wěn)定排序,且平均時間復(fù)雜度均為O(nlogn)。區(qū)別有二:
(1)堆屬于選擇類排序,快速排序?qū)儆诮粨Q類排序;
(2)堆排序一般優(yōu)于快速排序的重要一點是,數(shù)據(jù)的初始分布情況對堆排序的效率沒有大的影響。
具體實現(xiàn)參考:十種常見排序算法。
七十二、反轉(zhuǎn)單鏈表
答:
ListNode* reverseList(ListNode* pHead){ListNode* pReverseHead=NULL;ListNode* pNode=pHead;ListNode* pPrev=NULL;while(pNode){ListNode* next=pNode->m_next; //先保存當(dāng)前被處理節(jié)點的下一個節(jié)點if(NULL==next){//原鏈表的最后一個節(jié)點pReverseHead=pNode;break;}pNode->m_next=pPrev;//該節(jié)點的前一個節(jié)點pPrev=pNode;pNode=next;}return pReverseHead; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
七十三、如何判斷單鏈表是否存在環(huán)?
答:
算法的思想是設(shè)定兩個指針p, q,其中p每次向前移動一步,q每次向前移動兩步。那么如果單鏈表存在環(huán),則p和q相遇;否則q將首先遇到null。
七十四、打印楊輝三角
#include <stdio.h> int main() {int a[10][10];int i,j;for(i=0;i<10;i++){a[i][0]=1;a[i][i]=1;}for(i=2;i<10;i++){for(j=1;j<i;j++)a[i][j]=a[i-1][j]+a[i-1][j-1];}for(i=0;i<10;i++){for(j=0;j<(10-i-1);++j)printf(" "); //每行前需要空的數(shù)的位置,每個數(shù)占4個空格for(j=0;j<=i;j++)printf("%4d ",a[i][j]);printf("\n");}return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
七十五、虛基類的作用是什么,虛基類的實現(xiàn)機制就是什么呢?
答:
虛基類的作用是在C++多重繼承的情況下,如果出現(xiàn)菱形繼承的話,為了消除在子類中出現(xiàn)父類數(shù)據(jù)實體的多份拷貝。
虛基類的實現(xiàn)機制這個有點復(fù)雜。不同編譯器內(nèi)部實現(xiàn)的機制也不相同。其主要有兩種實現(xiàn)方案:
(1)是引入virtual base class table,不管多少個虛基類,總是只有一個指針指向它,這個virtual base class table(VBTBL)包括真正的 virtual base class 指針。
(2)Bjarne的辦法是在virtual function table中放置virtual base class的offset,而非地址,這個offset在virtual function table 的負(fù)位置(正值是索引virtual function,而負(fù)值則將方向盤引到virtual base class offsets)。
在VC++中,采用的是類似第一種方案。對每個繼承自虛基類的類實例,將增加一個隱藏的“虛基類表指針”(vbptr)成員變量,從而達(dá)到間接計算虛基類位置的目的。該變量指向一個全類共享的偏移量表,表中項目記錄了對于該類而言,“虛基類表指針”與虛基類之間的偏移量”,而不是真正的 virtual base class 指針,這就是說類似于上面的第一種方案,而非嚴(yán)格按照該方案。具體參見C++虛繼承對象的內(nèi)存布局。
對于g++,實現(xiàn)上和VC++不同,它并沒有生成獨立的虛基類表和虛基類表指針來指明虛基類的偏移地址,具體實現(xiàn)細(xì)節(jié)我還不太清楚,可能《深度探索c++對象模型》會有說明。這是只是測試了當(dāng)子類存在虛函數(shù)表指針和虛函數(shù)表時,編譯器并不會為子類對象實體生成額外的一個虛基類表指針。
但是當(dāng)子類沒有虛函數(shù)表指針時,編譯器會為子類對象生成一個指針變量,這個指針變量很可能就是指向虛基類表。種種跡象表明g++的實現(xiàn)方案和上面提到的第二種方案很相似,具體我沒有深入研究其對象布局,以后再探討我猜測的真?zhèn)巍?/p>
七十六、C++的單例模式
class CSingleton { private: CSingleton() //構(gòu)造函數(shù)是私有的 { } static CSingleton *m_pInstance; public: static CSingleton * GetInstance() { if(m_pInstance == NULL) //判斷是否第一次調(diào)用 m_pInstance = new CSingleton(); return m_pInstance; } };- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
七十七、數(shù)按位顛倒
七十八、TCP/IP 流量控制和擁塞控制
七十九、arp攻擊
arp 攻擊是:
ARP攻擊就是通過偽造IP地址和MAC地址實現(xiàn)ARP欺騙,能夠在網(wǎng)絡(luò)中產(chǎn)生大量的ARP通信量使網(wǎng)絡(luò)阻塞,攻擊者只要持續(xù)不斷的發(fā)出偽造的ARP響應(yīng)包就能更改目標(biāo)主機ARP緩存中的IP-MAC條目,造成網(wǎng)絡(luò)中斷或中間人攻擊。
八十、KMP的時間復(fù)雜度
O(m+n)
八十一、并查集
原問題描述:有A、B兩個犯罪團伙,給定n個人,然后給定m組關(guān)系,比如:r(1,2),代表1和2號犯人不在一個團伙中,會隨機問給定的兩個人在不在一個組織中,比如:q(x1,x2)。回答:是就是,不是就不是,不知道就不知道
八十二、如何在O(1)時間下,不申請空間前提下,刪除鏈表的某個節(jié)點。
交換后面的節(jié)點和當(dāng)前節(jié)點,刪除下一個節(jié)點。但是對于最后一個節(jié)點,只能從前往后找到pre節(jié)點,這樣的復(fù)雜度是O(n),但是平均復(fù)雜度是O(1)
八十三、交換值
a = a^b; b = a^b; a = a^b;- 1
- 2
- 3
- 1
- 2
- 3
或者如下:
a = a + b; b = a - b; a = a - b;- 1
- 2
- 3
- 1
- 2
- 3
八十四、如果socket建立連接后,發(fā)送50000字節(jié),那邊可以接收到多少個?
根據(jù)真實的網(wǎng)絡(luò)環(huán)境,那邊可能會接收到不定的字節(jié)。
八十五、為什么拷貝構(gòu)造函數(shù)要用引用
不然會一直無限傳值,直到棧溢出。
八十六、如何讓一個類不能被繼承
構(gòu)造函數(shù)私有化
八十七、分頁算法和頁內(nèi)組織形式
八十八、如何判斷大端小端模式
八十九、如下代碼有什么問題?
class CTest { public:int a;CTest() {memset(this,0,sizeof(*this)); }void printSize() {cout<<sizeof(*this)<<endl;} };int main() {//freopen("input.txt","r",stdin);CTest t;//t.printSize();return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
引申:this指針放在哪里?
九十、strcpy、memcpy、memmove區(qū)別?strcpy的結(jié)束標(biāo)志是什么?
九十一、new做了什么?重載operator new應(yīng)該注意什么?
new分配內(nèi)存+調(diào)用構(gòu)造函數(shù)
注意事項:
(1)重載 operator new 的同時也需要一并重載 operator delete,因為如果調(diào)用 delete 是默認(rèn)的delete,這樣你在new重載里面做的事情只有你自己知道,默認(rèn)的不會去做,就會付出慘重的代價。
//這不是個很簡單的事(詳細(xì)參考《Effective C++ Third Edition》 Item 51)。operator new 通常這樣編寫:// 這里并沒有考慮多線程訪問的情況 void* operator new(std::size_t size) throw(std::bad_alloc) {using namespace std;// size == 0 時 new 也必須返回一個合法的指針if (size == 0)size = 1;while (true) {//嘗試進行內(nèi)存的分配if (內(nèi)存分配成功)return (成功分配的內(nèi)存的地址);// 內(nèi)存分配失敗時,查找當(dāng)前的 new-handling function// 因為沒有直接獲取到 new-handling function 的辦法,因此只能這么做new_handler globalHandler = set_new_handler(0);set_new_handler(globalHandler);// 如果存在 new-handling function 則調(diào)用if (globalHandler) (*globalHandler)();// 不存在 new-handling function 則拋出異常else throw std::bad_alloc();} }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
(2)不要輕易重載全局 new(文件作用域)
重載 ::operator new() 的理由
Effective C++ 第三版第 50 條列舉了定制 new/delete 的幾點理由:
檢測代碼中的內(nèi)存錯誤
優(yōu)化性能
獲得內(nèi)存使用的統(tǒng)計數(shù)據(jù)
這些都是正當(dāng)?shù)男枨?#xff0c;但是不重載 ::operator new() 也能達(dá)到同樣的目的。
http://www.360doc.com/content/12/1211/17/9200790_253442412.shtml
九十二、如下代碼sizeof輸出多少?如何強制內(nèi)存對齊?
struct TStruct {int a;char b;double c; };- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
跟順序相關(guān)。。。
1、結(jié)構(gòu)體成員變量相對于結(jié)構(gòu)體對象的首地址的偏移量是該成員變量類型大小的整數(shù)倍。
2、整個結(jié)構(gòu)體對象的最終大小是結(jié)構(gòu)體成員變量中類型大小最大的整數(shù)倍。
struct tmp {int aa;int bb;int cc; };struct tree {char c;double b;int a;tmp ss; };int main() {cout << sizeof(tmp) << endl;cout << sizeof(tree) << endl;return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
結(jié)果顯示:
12
32
1+7+8+4+4+4+4 = 32
另:不要把被嵌套的結(jié)構(gòu)體當(dāng)成一個整體
如下代碼:
struct tmp {int aa;char bb;double cc; };struct TStruct {char c;double b;int a;tmp ss; };int main() {cout << sizeof(tmp) << endl;cout << sizeof(TStruct) << endl;return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
輸出:
16
40
1+7+8+4+4+1+7+8 = 40
九十三、int a[100]; a+3是加了多少?
int a[100]; cout<<a<<endl; cout<<a+3<<endl;- 1
- 2
- 3
- 1
- 2
- 3
實測加了12
九十四、__stdcall、fastcall、__cdecl
調(diào)用規(guī)范簡介
首先,要實現(xiàn)函數(shù)調(diào)用,除了要知道函數(shù)的入口地址外,還要向函數(shù)傳遞合適的參數(shù)。向被調(diào)函數(shù)傳遞參數(shù),可以有不同的方式實現(xiàn)。這些方式被稱為“調(diào)用規(guī)范”或“調(diào)用約定”。C/C++中常見的調(diào)用規(guī)范有__cdecl、__stdcall、__fastcall和__thiscall。
__cdecl調(diào)用約定
又稱為C調(diào)用約定,是C/C++默認(rèn)的函數(shù)調(diào)用約定,它的定義語法是:
- 1
- 2
- 1
- 2
約定的內(nèi)容有:
(1)參數(shù)入棧順序是從右向左;
(2)在被調(diào)用函數(shù) (Callee) 返回后,由調(diào)用方 (Caller)調(diào)整堆棧。
由于這種約定,C調(diào)用約定允許函數(shù)的參數(shù)的個數(shù)是不固定的,這也是C語言的一大特色。因為每個調(diào)用的地方都需要生成一段清理堆棧的代碼,所以最后生成的目標(biāo)文件較__stdcall、__fastcall調(diào)用方式要大,因為每一個主調(diào)函數(shù)在每個調(diào)用的地方都需要生成一段清理堆棧的代碼。
__stdcall調(diào)用約定
又稱為標(biāo)準(zhǔn)調(diào)用約定,申明語法是:
- 1
- 1
約定的內(nèi)容有:
(1)參數(shù)從右向左壓入堆棧;
(2)函數(shù)自身清理堆棧;
(3)函數(shù)名自動加前導(dǎo)的下劃線,后面緊跟一個@符號,其后緊跟著參數(shù)的尺寸;
(4)函數(shù)參數(shù)個數(shù)不可變。
__fastcall調(diào)用約定
又稱為快速調(diào)用方式。和__stdcall類似,它約定的內(nèi)容有:
(1) 函數(shù)的第一個和第二個DWORD參數(shù)(或者尺寸更小的)通過ecx和edx傳遞,其他參數(shù)通過從右向左的順序壓棧;
(2)被調(diào)用函數(shù)清理堆棧;
(3)函數(shù)名修改規(guī)則同stdcall。
其聲明語法為:
int __fastcall function(int a,int b);- 1
- 1
注意:不同編譯器編譯的程序規(guī)定的寄存器不同。在Intel 386平臺上,使用ECX和EDX寄存器。使用__fastcall方式無法用作跨編譯器的接口。
__thiscall調(diào)用約定
是唯一一個不能明確指明的函數(shù)修飾,因為thiscall不是關(guān)鍵字。它是C++類成員函數(shù)缺省的調(diào)用約定。由于成員函數(shù)調(diào)用還有一個this指針,因此必須特殊處理,thiscall意味著:
(1) 參數(shù)從右向左入棧;
(2) 如果參數(shù)個數(shù)確定,this指針通過ecx傳遞給被調(diào)用者;如果參數(shù)個數(shù)不確定,this指針在所有參數(shù)壓棧后被壓入堆棧;
(3)對參數(shù)個數(shù)不定的,調(diào)用者清理堆棧,否則函數(shù)自己清理堆棧。
http://blog.csdn.net/K346K346/article/details/47398243
九十五、工廠模式、裝飾者模式、觀察者模式類圖?MVC中M、V、C是單向的還是雙向
1、工廠模式
抽象工廠
九十六、編譯器斷點如何實現(xiàn)的
九十七、debug和release的區(qū)別是什么?為什么有時候在debug模式下運行無誤,但在release模式下出錯?編譯器debug模式在可執(zhí)行文件中加入了什么信息
(1)debug 中放入調(diào)試信息,比如符號表
(2)debug 是代碼直譯,release 可能有優(yōu)化,比如寄存器易失問題。不用的變量去掉
九十八、如何最快把一個整數(shù)拆成3個小于它的 c 整數(shù)
九十九、ipc的幾種方式
(1)管道PIPE和有名管道FIFO – 比如shell的重定向
(2)信號signal – 比如殺死某些進程kill -9,比如忽略某些進程nohup ,信號是一種軟件中斷
(3)消息隊列 – 相比共享內(nèi)存會慢一些,緩沖區(qū)有限制,但不用加鎖,適合命令等小塊數(shù)據(jù)。
(4)共享內(nèi)存 – 最快的IPC方式,同一塊物理內(nèi)存映射到進程A、B各自的進程地址空間,可以看到對方的數(shù)據(jù)更新,需要注意同步機制,比如互斥鎖、信號量。適合傳輸大量數(shù)據(jù)。
(5)信號量 – PV操作,生產(chǎn)者與消費者示例;
(6)套接字 – socket網(wǎng)絡(luò)編程,網(wǎng)絡(luò)中不同主機間的進程間通信,屬高級進程間通信。
一百、線程同步的方式
(1)互斥量(mutex)、(2)條件變量、(3)信號量。 如同進程一樣,線程也可以通過信號量來實現(xiàn)同步,雖然是輕量級的。
這里要注意一點,互斥量可通過pthread_mutex_setpshared接口設(shè)置可用于進程間同步;條件標(biāo)量在初始化時,也可以通過接口pthread_condattr_setpshared指定該條件變量可用于進程進程間同步。
一百零一、Unicode utf ansi具體是如何編碼的
utf-8 中文字符有些字符占2個有些3個,英文字符都是1個;ucs2 統(tǒng)一是2個
一百零二、冒泡復(fù)雜度、改進冒泡
一百零三、計算快排和堆排復(fù)雜度?最好的排序算法?
一百零四、如何檢測內(nèi)存泄漏?
借鑒java的引用計數(shù)器
一百零五、如下 a 和 b 哪個地址大?
int main() {int a;int b;cout<<&a<<endl;cout<<&b<<endl;return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
a地址更大,a先入棧。
一百零六、define 和 typedef 的區(qū)別
1、定義
define是宏定義,在預(yù)處理階段進行字符串替換。
typedef是關(guān)鍵字,聲明類型別名,在編譯時處理(增加可讀性)。
2、define相當(dāng)于單純的字符串替換
#define int_ptr int* //不能顛倒位置 int_ptr a,b; //相當(dāng)于 int *a,b; a是復(fù)合類型int *,b 只是int- 1
- 2
- 1
- 2
- 1
- 2
- 1
- 2
3、跟const連用
typedef int* pint; #define PINT int* const pint P; //相當(dāng)于int* const P(const 修飾 int *),P的指向不能改 const PINT P; //相當(dāng)于const int* P,P指向的內(nèi)容不能改- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
4、typedef 語法上是一個存儲類的關(guān)鍵字,如 static、auto、extern、mutable、register。
typedef static int INT2; //編譯失敗,指定了一個以上的存儲類- 1
- 1
一般遵循:
define 定義“可讀”的變量以及一些宏語句任務(wù)(包括無參和帶參)
typedef 常用來定義關(guān)鍵字,冗長的類型的別名
一百零七、ofstream
一百零八、32位/64位 基本類型變量會不同嗎
是的,c++基本數(shù)據(jù)類型的變量占據(jù)內(nèi)存字節(jié)數(shù)的多少根運行平臺有關(guān)。
一百零九、如果用0-9、a、b、c表示32進制的整數(shù),那10進制數(shù)873085表示的是什么?
一百一十、如果父類指針指向子類,開始運行沒錯,但是后來崩潰了,可能原因是什么?
可能這個指針指向的空間已被釋放,或者是這個函數(shù)里面出錯了。
一百一十一、可否寫一個程序掃描整個內(nèi)存空間?
一百一十二、編譯鏈接做了什么
目標(biāo)程序又稱“目的程序”
由編譯程序?qū)⒃闯绦蚓幾g成與之等價的由機器碼構(gòu)成的,計算機能直接運行的程序,該程序叫目標(biāo)程序。
鏈接器 (linker)
將一個個的目標(biāo)文件 ( 或許還會有若干程序庫 ) 鏈接在一起生成一個完整的可執(zhí)行文件。
在符號解析 (symbol resolution) 階段,鏈接器按照所有目標(biāo)文件和庫文件出現(xiàn)在命令行中的順序從左至右依次掃描它們,在此期間它要維護若干個集合 :
(1) 集合 E 是將被合并到一起組成可執(zhí)行文件的所有目標(biāo)文件集合;
(2) 集合 U 是未解析符號 (unresolved symbols ,比如已經(jīng)被引用但是還未被定義的符號 ) 的集合;
(3) 集合 D 是所有之前已被加入到 E 的目標(biāo)文件定義的符號集合。一開始, E 、 U 、 D 都是空的。
鏈接器的工作過程:
(1): 對命令行中的每一個輸入文件 f ,鏈接器確定它是目標(biāo)文件還是庫文件,如果它是目標(biāo)文件,就把 f 加入到 E ,并把 f 中未解析的符號和已定義的符號分別加入到 U 、 D 集合中,然后處理下一個輸入文件。
(2): 如果 f 是一個庫文件,鏈接器會嘗試把 U 中的所有未解析符號與 f 中各目標(biāo)模塊定義的符號進行匹配。如果某個目標(biāo)模塊 m 定義了一個 U 中的未解析符號,那么就把 m 加入到 E 中,并把 m 中未解析的符號和已定義的符號分別加入到 U 、 D 集合中。不斷地對 f 中的所有目標(biāo)模塊重復(fù)這個過程直至到達(dá)一個不動點 (fixed point) ,此時 U 和 D 不再變化。而那些未加入到 E 中的 f 里的目標(biāo)模塊就被簡單地丟棄,鏈接器繼續(xù)處理下一輸入文件。
(3): 如果處理過程中往 D 加入一個已存在的符號 ,或者當(dāng)掃描完所有輸入文件時 U 非空,鏈接器報錯并停止動作。否則,它把 E 中的所有目標(biāo)文件合并在一起生成可執(zhí)行文件。
這種”翻譯”通常有兩種方式,即編譯方式和解釋方式。編譯方式是指利用事先編好的一個稱為編譯程序的機器語言程序,
作為系統(tǒng)軟件存放在計算機內(nèi),當(dāng)用戶將高級語言編寫的源程序輸入計算機后,編譯程序便把源程序整個地翻譯成用
機器語言表示的與之等價的目標(biāo)程序,然后計算機再執(zhí)行該目標(biāo)程序,以完成源程序要處理的運算并取得結(jié)果。
解釋方式是指源程序進入計算機后,解釋程序邊掃描邊解釋,逐句輸入逐句翻譯,計算機一句句執(zhí)行,并不產(chǎn)生目標(biāo)程序。
http://blog.chinaunix.net/uid-11572501-id-2868702.html
一百一十三、可變參函數(shù)
#include <stdarg.h> // 必須包含的頭文件int Add(int start,...) // ...是作為占位符 { va_list arg_ptr; // 定義變參起始指針int sum=0; // 定義變參的和int nArgValue =start; // va_start(arg_ptr,start); // arg_ptr指向第一個變參do {sum+=nArgValue; // 求和nArgValue = va_arg(arg_ptr,int); // arg_ptr指向下一個變參} while(nArgValue != 0); // 判斷結(jié)束條件;結(jié)束條件是自定義為=0時結(jié)束va_end(arg_ptr); // 復(fù)位指針return sum; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
函數(shù)的調(diào)用方法為Add(1,2,3,0);這樣,必須以0結(jié)尾,因為變參函數(shù)結(jié)束的判斷條件就是讀到0停止。
解釋:
所使用到的宏:
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
typedef char * va_list;
#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( (t )((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )
1、首先把va_list被定義成char*,這是因為在我們目前所用的PC機上,字符指針類型可以用來存儲內(nèi)存單元地址。而在有的機器上va_list是被定義成void*的
2、定義_INTSIZEOF(n)主要是為了某些需要內(nèi)存的對齊的系統(tǒng).這個宏的目的是為了得到最后一個固定參數(shù)的實際內(nèi)存大小。在我的機器上直接用sizeof運算符來代替,對程序的運行結(jié)構(gòu)也沒有影響。(后文將看到我自己的實現(xiàn))。
3、va_start的定義為 &v+_INTSIZEOF(v) ,這里&v是最后一個固定參數(shù)的起始地址,再加上其實際占用大小后,就得到了第一個可變參數(shù)的起始內(nèi)存地址。所以我們運行va_start(ap, v)以后,ap指向第一個可變參數(shù)在的內(nèi)存地址,有了這個地址,以后的事情就簡單了。
這里要知道兩個事情:
⑴在intel+windows的機器上,函數(shù)棧的方向是向下的,棧頂指針的內(nèi)存地址低于棧底指針,所以先進棧的數(shù)據(jù)是存放在內(nèi)存的高地址處。
(2)在VC等絕大多數(shù)C編譯器中,默認(rèn)情況下,參數(shù)進棧的順序是由右向左的,因此,參數(shù)進棧以后的內(nèi)存模型如下圖所示:最后一個固定參數(shù)的地址位于第一個可變參數(shù)之下,并且是連續(xù)存儲的。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
(4) va_arg():有了va_start的良好基礎(chǔ),我們?nèi)〉昧说谝粋€可變參數(shù)的地址,在va_arg()里的任務(wù)就是根據(jù)指定的參數(shù)類型取得本參數(shù)的值,并且把指針調(diào)到下一個參數(shù)的起始地址。
因此,現(xiàn)在再來看va_arg()的實現(xiàn)就應(yīng)該心中有數(shù)了:
#define va_arg(ap,t) ( (t )((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
這個宏做了兩個事情,
①用用戶輸入的類型名對參數(shù)地址進行強制類型轉(zhuǎn)換,得到用戶所需要的值
②計算出本參數(shù)的實際大小,將指針調(diào)到本參數(shù)的結(jié)尾,也就是下一個參數(shù)的首地址,以便后續(xù)處理。
(5)va_end宏的解釋:x86平臺定義為ap=(char*)0;使ap不再 指向堆棧,而是跟NULL一樣.有些直接定義為((void*)0),這樣編譯器不會為va_end產(chǎn)生代碼,例如gcc在Linux的x86平臺就是這樣定義的. 在這里大家要注意一個問題:由于參數(shù)的地址用于va_start宏,所以參數(shù)不能聲明為寄存器變量或作為函數(shù)或數(shù)組類型. 關(guān)于va_start, va_arg, va_end的描述就是這些了,我們要注意的 是不同的操作系統(tǒng)和硬件平臺的定義有些不同,但原理卻是相似的.
一百一十四、函數(shù)簽名
C++函數(shù)簽名包括 函數(shù)名、參數(shù)個數(shù)、參數(shù)類型
不包括返回類型
c語言函數(shù)簽名只包含函數(shù)名
一百一十五、要實現(xiàn)一個云盤,應(yīng)該如何分模塊?
個人觀點:
按照用戶動作流來劃分是最直觀的。
client端有:文件安全監(jiān)測模塊,md5生成模塊
傳輸模塊
server端有:存儲模塊(包括文件副本計數(shù)器+冗余備份)、用戶模塊(用戶管理+用戶文件對應(yīng)表)
一百一十六、云盤如何判斷一個文件安全性?
后綴名+病毒特征碼
一百一十七、云盤傳輸過程中,應(yīng)該選擇TCP還是UDP?如何選擇第5層協(xié)議?
udp可以手工實現(xiàn)重傳。
一百一十八、給一個10T硬盤空間,在沒有文件系統(tǒng)的情況下,云盤文件應(yīng)該如何組織?最好不要用到db。
這里需要說明一下,有沒有文件系統(tǒng),跟程序得到的地址是不是虛擬地址沒有關(guān)系。虛擬地址和物理地址的映射關(guān)系是內(nèi)核做的。
一百一十九、用戶和文件如何組織?還有用戶可能對應(yīng)目錄和文件,目錄可以嵌套,應(yīng)該如何組織?
一百二十、用戶云盤上傳下載慢,可能原因有哪些?
1、沒充會員
2、運營商不同
3、ddos攻擊
4、服務(wù)器和用戶太遠(yuǎn),路由跳數(shù)太多
5、服務(wù)器訪問量太大
一百二十一、如何監(jiān)測磁盤瞬時讀寫量過大?
一百二十二、STL容器
(1)標(biāo)準(zhǔn)STL序列容器:vector、string、deque(雙向)、list(雙向)
(2)標(biāo)準(zhǔn)STL關(guān)聯(lián)容器:set(值唯一)、multiset、map、multimap
(3)非標(biāo)準(zhǔn)序列容器:slist(單向)和 rope
(4)非標(biāo)準(zhǔn)的關(guān)聯(lián)容器:hash_set、hash_multiset、hash_map、hash_multimap
(5)幾種標(biāo)準(zhǔn)的非STL容器:數(shù)組、bitset、stack、queue 和 priority_queue
關(guān)于容器內(nèi)存存儲方式
(1)連續(xù)內(nèi)存容器:元素放在一或多塊內(nèi)存,每塊內(nèi)存多個元素,插入或刪除時,同一內(nèi)存塊其他元素也會發(fā)生移動, vector,string,deque,rope
(2)基于節(jié)點的容器:內(nèi)個內(nèi)存塊只放一個元素,list,slist,標(biāo)準(zhǔn)關(guān)聯(lián)容器,非標(biāo)準(zhǔn)的哈希容器。
建議:
(1)vector、deque、list:vector默認(rèn),序列中間頻繁插入刪除,用list,頭尾插入刪除,用deque。
(2)任意位置插入:選擇序列容器
(3)隨機訪問迭代器:用vector、deque、string、rope
(4)考慮元素查找速度:hash容器、排序vector,標(biāo)準(zhǔn)關(guān)聯(lián)容器
(5)插入刪除,需要事務(wù)語義:基于節(jié)點(每個內(nèi)存塊只放一個元素,list、slist)
一百二十三、struct和class
struct:默認(rèn)public(成員/繼承),編譯器不為struct保留內(nèi)存空間。
class:默認(rèn) private(成員/繼承),為類保留
兩者都可以繼承、構(gòu)造/析構(gòu)、多態(tài)
一百二十四、cpu的位數(shù),操作系統(tǒng)的位數(shù),編譯器中編譯的位數(shù)有什么關(guān)聯(lián)
一百二十五、一共有10t貨物,裝到若干箱子中,箱子最多裝1t貨物(不考慮箱子數(shù)量)。現(xiàn)在有載重3t的貨車,問至少要多少輛車一定能裝完這些貨物?
http://www.mofangge.com/html/qDetail/02/c0/201310/b88kc002360282.html
有幾個地方要想通:
(1)不是4就是5
(2)每個箱子平均比不平均更容易造成4輛車裝不完的局面
(3)在(2)成立的條件下,如果分的箱子越少,越容易造成4輛車裝不完,因為顆粒越小越容易勻開。
(4)在(2)成立條件下,必須不能被4整除,否則每輛車2.5噸一定可以搞定。
這樣就可以列出下面的編程公式:
y代表箱子的個數(shù),那么 10/y 代表每個箱子重量。
通過編程累加y到一個上限(個人感覺100足以),如果存在y,就證明4輛裝不完。
可以求出,y為13的時候成立
一百二十六、樹和hash_map的優(yōu)缺點
一百二十七、效率優(yōu)先的情況下,輸入一些0-99999的數(shù),實現(xiàn)一個數(shù)據(jù)結(jié)構(gòu),高效完成刪除、插入、查找、遍歷、計數(shù)個數(shù)
一百二十八、MIN_INT
今天在看《深入理解計算機系統(tǒng)》的時候,在p105頁作者給出了INT_MIN在標(biāo)準(zhǔn)頭文件limits.h中的定義
#define INT_MAX 2147483647 #define INT_MIN (-INT_MAX - 1)- 1
- 2
- 1
- 2
這里沒有簡單地將INT_MIN賦值成-2147483647,是因為-2147483648對于編譯器而言是個表達(dá)式,而2147483648對于32-bit整數(shù)是無法表示的,所以經(jīng)過這個表達(dá)式的結(jié)果是未定義的。在GCC上直接寫-2147483648后,編譯器給出了警告,說結(jié)果是unsigned。
這里有一篇文章提到了其中的緣由,可以參考:INT_MIN
十六進制輸入,應(yīng)該輸入的就是計算機的存放方式,是補碼存放。
比如:
int main() {//freopen("input.txt","r",stdin);int a = INF;int maxInt = 0x7fffffff; //最大intint minusOne = 0xffffffff; //-1int minInt = 0x80000000; //最小intint minusMaxInt = 0x80000001; //-maxIntreturn 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
http://blog.csdn.net/seizef/article/details/7605010
一百二十九、如何傳入一個數(shù)組,而不退化為指針?
void func(int (&b) [20][10]) { //通過數(shù)組的引用 }void func(int *b) {}int main() { //freopen("input.txt","r",stdin); int a[20][10]; a[2][1] = 5; func(a); return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
現(xiàn)在又有一個問題:如果構(gòu)成指針和引用數(shù)組這樣的重載,到底先進哪一個?
我的思考是,應(yīng)該進引用那個,因為類似于 int 和 double ,func(5) 會優(yōu)先選用 int,就在于數(shù)組a類型原本是數(shù)組,所以會優(yōu)先用原類型。
引用的確是傳的數(shù)組,而傳值是傳遞的指針,編譯器將其退化了
如果是傳引用,調(diào)試的時候有寬度限制(行下標(biāo)從0-19),這就證明是數(shù)組,打印sizeof是20*10*4= 800
如果是指針,只有一個指針的地址,而且打印sizeof也是一個指針的值:
一百三十、如何返回一個數(shù)組?
一百三十一、可不可以將int b[4]傳給參數(shù)int a[5]
答案是:傳值可以,引用不行
數(shù)組傳值那邊退化為指針
一百三十二、關(guān)于數(shù)組越界
不論讀寫都不會報錯,測試平臺vs2012
那當(dāng)數(shù)組影響到其他變量的時候會不會報錯呢,比如在函數(shù)棧中定義一些其他變量,數(shù)組越界寫到別的地方。
還真寫進去了(可能會崩潰),也就是說,數(shù)組底層跟指針是類似的,可以任意訪問非法空間。
一百三十三、一個程序一直進不了main函數(shù),可能原因是什么?
main函數(shù)不一定是第一個運行的函數(shù),比如全局類的構(gòu)造函數(shù)。
一百三十四、c++中可以打印當(dāng)前函數(shù)地址嗎?
一百三十五、vs中條件斷點+內(nèi)存斷點
一百三十六、什么是線程安全
如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結(jié)果和單線程運行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣的,就是線程安全的。
或者說:一個類或者程序所提供的接口對于線程來說是原子操作或者多個線程之間的切換不會導(dǎo)致該接口的執(zhí)行結(jié)果存在二義性,也就是說我們不用考慮同步的問題。
線程安全問題都是由全局變量及靜態(tài)變量引起的。
若每個線程中對全局變量、靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執(zhí)行寫操作,一般都需要考慮線程同步,否則的話就可能影響線程安全。
1.局部變量局部使用是安全的
為什么?因為每個thread 都有自己的運行堆棧,而局部變量是生存在堆棧中,大家不干擾。
所以代碼1
int local1;
++local1;
是安全的
2.全局原生變量多線程讀寫是不安全的
全局變量是在堆(heap)中
long global1 = 0;
++global2;
++這個操作其實分為兩部,一個是讀,另外一個是寫
mov ecx,global
add ecx,1
mov global,ecx
所以代碼3處是不安全的
3.函數(shù)靜態(tài)變量多線程讀寫也是不安全的
道理同2
所以代碼2處也是不安全的
4.volatile能保證全局整形變量是多線程安全的么
不能。
volatile僅僅是告誡compiler不要對這個變量作優(yōu)化,每次都要從memory取數(shù)值,而不是從register
所以代碼4也不是安全
5.InterlockedIncrement保證整型變量自增的原子性
所以代碼5是安全的
6.function static object的初始化是多線程安全的么
不是。
著名的Meyer Singleton其實不是線程安全的
Object & getInstance()
{
static Object o;
return o;
}
可能會造成多次初始化對象
所以代碼6處是不安全的
7.在32機器上,4字節(jié)整形一次assign是原子的
比如
i =10; //thread1
i=4; //thread2
不會導(dǎo)致i的值處于未知狀態(tài),要么是10要么是4
寫好多線程安全的法寶就是封裝,使數(shù)據(jù)有保護的被訪問到
安全性:
局部變量>成員變量>全局變
一百三十七、RTTI
一百三十八、java循環(huán)左移、右移
此題在編程之美中用到,截圖留念:
byte a=112,用程序?qū)崿F(xiàn),將其循環(huán)左移三位和右移三位。
112的二進制原碼:0111 0000
112循環(huán)左移3位后的二進制碼:1000 0011
112循環(huán)右移3位后的二進制碼:0000 1110
先將循環(huán)左移的程序代碼告訴大家:
public class TestCircle{public static void main(String args[]){byte x=112;System.out.println((byte)(x<<3|x>>5));} }- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
程序的輸出結(jié)果是-125,它的原碼為1111 1101,補碼為1000 0011(正好是112循環(huán)左移三位后的數(shù)字)
再看循環(huán)右移的程序代碼:
public class TestCircle{public static void main(String args[]){byte x=112;System.out.println((byte)(x>>3|x<<5));} }- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
程序的輸出結(jié)果是14,他的原碼、補碼相同都是0000 1110(正好是112循環(huán)右移三位后的數(shù)字)
總結(jié):對于一個數(shù)據(jù)類型長度為L的數(shù)據(jù)n,對其進行循環(huán)左移m位(或右移m位),只需將數(shù)據(jù)n左移(或右移)m位的結(jié)果和數(shù)據(jù)n右移(或左移)L-m位的結(jié)果進行或運算,再將或運算的結(jié)果強制轉(zhuǎn)換為原類型即可。
一百三十九、printf(“abc”+1);
答案是輸出”bc”,可能是一個字符數(shù)組收地址,后面加的數(shù)字代表移動的位數(shù)
一百四十. 遍歷刪除stl迭代器引發(fā)的錯誤
(1)對于節(jié)點式容器(map, list, set)元素的刪除,插入操作會導(dǎo)致指向該元素的迭代器失效,其他元素迭代器不受影響;
(2)對于順序式容器(vector,string,deque)元素的刪除、插入操作會導(dǎo)致指向該元素以及后面的元素的迭代器失效。
http://ivan4126.blog.163.com/blog/static/209491092201351441333357/
正確刪除法一:
(1)當(dāng)刪除特定值的元素時,刪除元素前保存當(dāng)前被刪除元素的下一個元素的迭代器。
map<string,int >::iterator nextIt=countMap.begin();for(map<string,int>::iterator it=countMap.begin(); ; ){if(nextIt!=countMap.end())++nextIt;else break;if(it->second==2){countMap.erase(it);}it=nextIt; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
(2)如何更加簡潔的實現(xiàn)該方法呢?下面給出該方法的《Effective STL》一書的具體實現(xiàn):
for(auto iter1 = theMap.begin(); iter1 != theMap.end(); ) {if(iter1->second == xxx){theMap.erase(iter1++); //#1 }else{++iter1;} }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
(3)也可以實現(xiàn)如下,利用函數(shù)返回值(返回下一個iterator)
for(map<string,int>::iterator it=countMap.begin();it!=countMap.end();){if(it->second==2){it=countMap.erase(it);}else++it; }- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
正確刪除法二:
使用 remove_copy_if 實現(xiàn)
當(dāng)刪除滿足某些條件的元素,可以使用remove_copy_if & swap方法。當(dāng)然,方法一的滿足特性值是滿足某些條件的特例,因此,也可以應(yīng)用此方法。
那么如何通過remove_copy_if 刪除 map< string,int>中的元素呢?網(wǎng)上很少有給出map的實現(xiàn),一般都是以vector為例。所以這里給出我的實現(xiàn)。
在通過remove_copy_if 按照條件拷貝了需要的元素之后,如何實現(xiàn)兩個map的交換,可以直接調(diào)用map的成員函數(shù)swap。參考代碼:
#include <iostream> #include <string> #include <map> #include <algorithm> #include <iterator> using namespace std;map<string,int> mapCount;//不拷貝的條件 bool notCopy(pair<string,int> key_value){return key_value.second==1; }int main(){mapCount.insert(make_pair("000",0));mapCount.insert(make_pair("001",1));mapCount.insert(make_pair("002",2));mapCount.insert(make_pair("003",1));map<string,int> mapCountTemp;//臨時map容器//之所以要用inserter()函數(shù)是因為通過調(diào)用insert()成員函數(shù)來插入元素,并由用戶指定插入位置remove_copy_if(mapCount.begin(),mapCount.end(),inserter(mapCountTemp,mapCountTemp.begin()),notCopy);mapCount.swap(mapCountTemp);//實現(xiàn)兩個容器的交換cout<<mapCount.size()<<endl; //輸出2cout<<mapCountTemp.size()<<endl; //輸出4//驗證//正確輸出://000 0//002 2for(map<string,int>::iterator it=mapCount.begin();it!=mapCount.end();++it){cout<<it->first<<" "<<it->second<<endl;}getchar(); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
這種方法的缺點:雖然實現(xiàn)兩個map的交換的時間復(fù)雜度是常量級,一般情況下,拷貝帶來的時間開銷會大于刪除指定元素的時間開銷,并且臨時map容器也增加了空間的開銷。
總結(jié):
關(guān)于容器的刪除,有篇blog總結(jié)的很好,現(xiàn)在轉(zhuǎn)貼如下:
刪除容器中具有特定值的元素:
(1)如果容器是vector、string或者deque,使用erase-remove的慣用法。如果容器是list,使用list::remove。如果容器是標(biāo)準(zhǔn)關(guān)聯(lián)容器,使用它的erase成員函數(shù)。
刪除容器中滿足某些條件的元素:
(2)如果容器是vector、string或者deque,使用erase-remove_if的慣用法。如果容器是list,使用list::remove_if。如果容器是標(biāo)準(zhǔn)關(guān)聯(lián)容器,使用remove_copy_if & swap 組合算法,或者自己協(xié)議個遍歷刪除算法。
參考資料:李健《編寫高質(zhì)量C++代碼》第七章,用好STL這個大輪子。
一百四十一、auto關(guān)鍵字
一百四十二、程序中分配的地址都是邏輯地址,那有沒有辦法用一個指針指向一個物理地址呢?
一百四十三、inline函數(shù)必須在頭文件中定義嗎?
所謂內(nèi)聯(lián)函數(shù),就是編譯器將函數(shù)定義({…}之間的內(nèi)容)在函數(shù)調(diào)用處展開,藉此來免去函數(shù)調(diào)用的開銷。
如果這個函數(shù)定義在頭文件中,所有include該頭文件的編譯單元都可以正確找到函數(shù)定義。然而,如果內(nèi)聯(lián)函數(shù)fun()定義在某個編譯單元A中,那么其他編譯單元中調(diào)用fun()的地方將無法解析該符號,因為在編譯單元A生成目標(biāo)文件A.obj后,內(nèi)聯(lián)函數(shù)fun()已經(jīng)被替換掉,A.obj中不再有fun這個符號,鏈接器自然無法解析。
所以,如果一個inline函數(shù)會在多個源文件中被用到,那么必須把它定義在頭文件中。在C++中,這意味著如果inline函數(shù)具有public或者protected訪問屬性,你就應(yīng)該這么做。
一百四十四、vector.clear() 函數(shù)
因為對于 vector.clear() 并不真正釋放內(nèi)存(這是為優(yōu)化效率所做的事),clear實際所做的是為vector中所保存的所有對象調(diào)用析構(gòu)函數(shù)(如果有的話),然后初始化size這些東西,讓你覺得把所有的對象清除了。。。
真正釋放內(nèi)存是在vector的析構(gòu)函數(shù)里進行的,所以一旦超出vector的作用域(如函數(shù)返回),首先它所保存的所有對象會被析構(gòu),然后會調(diào)用allocator中的deallocate函數(shù)回收對象本身的內(nèi)存。。。
所以你clear后還能訪問到對象數(shù)據(jù)(因為它根本沒清除),至于上面這位仁兄所指出的也有道理,在一些比較新的C++編譯器上(例如VS2008),當(dāng)進行數(shù)組引用時(例如a[2]這種用法),STL庫中會有一些check函數(shù)根據(jù)當(dāng)前容器的size值來判斷下標(biāo)引用是否超出范圍,如果超出,則會執(zhí)行這樣一句:
_THROW(out_of_range, “invalid vector subscript”);
即拋出一個越界異常,你clear后沒有捕獲異常,程序在新編譯器編譯后就會崩潰掉。。。。
一百四十五、變參函數(shù)
一百四十六、能否實現(xiàn):函數(shù)指針作為參數(shù),但傳遞過去的函數(shù)不一樣
因為在寫項目的時候,完成了很多諸如,void recurs_GetId(logic_TreeNode *some,std::vector<int> & L); 的函數(shù),但是不具有一般性。現(xiàn)在希望完成一個函數(shù),void recurs_DoSth(); 可以傳遞進一個簡單操作的函數(shù),就可以完成任務(wù)?
一百四十七、宏函數(shù)可以有返回值嗎?
以下代碼在vs下無法編譯,未在gcc平臺編譯:
#include <iostream>#define KADDR(addr) / ({ int tmp = addr; / if (addr > 5)/ tmp = 2;/ else/ tmp = 3;/ (addr + tmp);/ })/ int main() { int addr = 4; int ans; ans = KADDR(addr); printf("%d./n", ans); addr = 8; ans = KADDR(addr); printf("%d./n", ans); return 0; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
輸出結(jié)果:7,10
上面的這段代碼定義了一個名叫KADDR的宏,它可以跟據(jù)輸入的addr數(shù)據(jù),對其進行不同的偏移,但是最巧妙的是,這樣子寫可以有把這個值返回到調(diào)用該宏的語句中。
一百四十八、net 命令
一百四十九、在程序中能夠使用指針獲取內(nèi)存的絕對物理地址嗎
一百五十、默認(rèn)參數(shù)是否只能在聲明處寫
默認(rèn)參數(shù),在C++中,只要是函數(shù)都可以有默認(rèn)參數(shù),默認(rèn)參數(shù)是寫在函數(shù)聲明里面的。
如:
void swap(int a = 10, int b= 20);//聲明//實現(xiàn) void swap(int a, int b) {... }- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
一百五十一、構(gòu)造函數(shù)會為對象分配空間嗎?
不會,構(gòu)造函數(shù)是在已經(jīng)分配好的空間上初始化對象成員。分配空間是在new的時候運行時分配的。
一百五十二、重載 new 時,需要實現(xiàn)函數(shù)的構(gòu)造函數(shù)嗎?
這條跟 91 條有重合,重載new 會調(diào)用兩次構(gòu)造函數(shù)
(1)重載的成員函數(shù) new 里面只能::new其他的類型,不能new這個類本身。
(2)如果需要創(chuàng)建這個類本身的話,應(yīng)該要用malloc, malloc在分配完內(nèi)存后,并不會初始化。
可能跟 placement new 有關(guān)。
一百五十三、placement new
一百五十四、debug 和 release 的區(qū)別,為什么有時 debug 可以跑,但是 release 下崩潰?
一百五十五、VC編譯選項 /ML /MLd /MT /MTd /MD /MDd之間的區(qū)別
一百五十六、內(nèi)存斷點 && 條件斷點
一百五十七、c++重載運算符
一百五十八、A* 算法
一百五十九、c++流
一百六十、maze && 八皇后 — 回溯 && 深搜 && 廣搜
http://blog.csdn.net/K346K346/article/details/51289478
一百六十一、函數(shù)指針作為參數(shù),可以接收簽名完全不同(參數(shù)個數(shù)、類型都不定)的函數(shù)地址嗎?
一百六十二、參數(shù)個數(shù)不定的函數(shù)(類似 printf)
http://www.cnblogs.com/VRS_technology/archive/2010/05/10/1732006.html
一百六十三、類成員變量定義為引用,注意事項
http://blog.csdn.net/aore2010/article/details/5870928
一百六十四、multimap 允許 key 相同
適用于多對一關(guān)系
一百六十五、Qt 資源
http://download.qt.io/
一百六十六、實時傳輸協(xié)議 RTP
一百六十七、c++ 復(fù)合數(shù)據(jù)類型
一百六十八、c++ 如何實現(xiàn)一個無法被繼承的類
一百六十九、二叉排序樹 BST
一百七十、KMP
一百七十一、TCP的流量控制和擁塞控制
一百七十二、TSP 旅行商問題(dp + 遞歸)
一百七十三、如何使經(jīng)過指定節(jié)點集的路徑最短
一百七十四、c++中的函數(shù)重載、隱藏、覆蓋、重寫的區(qū)別
一百七十五、卡特蘭數(shù)
一百七十六、電子郵件協(xié)議以及工作原理
一百七十七、注冊表詳解
一百七十八、setjmp longjmp
一百七十九、千萬不要重載 || &&
一百八十、c++對象模型 && 內(nèi)存布局
一百八十一、守護進程
一百八十二、虛調(diào)用
一百八十三、c++為什么不加入垃圾回收?
一百八十四、main 函數(shù)參數(shù)中字符數(shù)組指向的空間在哪
https://www.quora.com/In-C-the-main-function-takes-a-pointer-to-an-integer-and-a-pointer-to-a-pointer-to-a-char-To-where-does-the-latter-point
一百八十五、進程 vs. 線程
http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001397567993007df355a3394da48f0bf14960f0c78753f000
一百八十六、HTTP 400/401/403/404/500 網(wǎng)頁錯誤代碼
http://blog.csdn.net/stuartjing/article/details/7579427
一百八十七、環(huán)境變量
一百八十八、include 命令包含頭文件時,會不會包含頭文件過多,生成空間過大
一百八十九、nginx
一百九十、4K對齊
一百九十一、C++中POD類型
一百九十二、預(yù)編譯指令 #pragma
一百九十三、賦值語句返回值
http://blog.csdn.net/jackbai1990/article/details/7467030
賦值語句的返回值是所賦的值,于是在C/C++中,就可以有如下的連續(xù)賦值語句:
b = a = 10;- 1
- 1
一百九十四、SYN Flood攻擊
一百九十五、Java學(xué)習(xí)計劃(不斷給更新)
一百九十六、Java反射機制
什么是反射
一般在開發(fā)針對java語言相關(guān)的開發(fā)工具和框架時使用,比如根據(jù)某個類的函數(shù)名字,然后執(zhí)行函數(shù),實現(xiàn)類的動態(tài)調(diào)用!
反射實例:
package test;import java.lang.reflect.*;/*** 基類Animal*/ class Animal {public Animal() {}public int location;protected int age;private int height;int length;public int getAge() {return age;}public void setAge(int age) {this.age = age;}private int getHeight() {return height;}public void setHeight(int height) {this.height = height;}}/*** 子類 —— Dog*/ class Dog extends Animal {public Dog() {}public String color;protected String name;private String type;String fur;}public class test {public static void main(String[] args) {System.out.println("Hello World!");testRefect();}private static void testRefect() {try {//返回類中的任何可見性的屬性(不包括基類)System.out.println("Declared fileds: ");Field[] fields = Dog.class.getDeclaredFields();for (int i = 0; i < fields.length; i++) {System.out.println(fields[i].getName());}//獲得類中指定的public屬性(包括基類)System.out.println("public fields: ");fields = Dog.class.getFields();for (int i = 0; i < fields.length; i++) {System.out.println(fields[i].getName());}//getMethod()只能調(diào)用共有方法,不能反射調(diào)用私有方法Dog dog = new Dog();dog.setAge(1);Method method1 = dog.getClass().getMethod("getAge", null);Object value = method1.invoke(dog);System.out.println("age: " + value);//調(diào)用基類的private類型方法/**先實例化一個Animal的對象 */Animal animal = new Animal();Class a = animal.getClass();//Animal中g(shù)etHeight方法是私有方法,只能使用getDeclaredMethodMethod method2 = a.getDeclaredMethod("getHeight", null);method2.setAccessible(true);//java反射無法傳入基本類型變量,可以通過如下形式int param = 12;Class[] argsClass = new Class[] { int.class };//Animal中g(shù)etHeight方法是共有方法,可以使用getMethodMethod method3 = a.getMethod("setHeight", argsClass);method3.invoke(animal, param);//Animal中height變量如果聲明為static變量,這樣在重新實例化一個Animal對象后調(diào)用getHeight(),還可以讀到height的值int height = (Integer) method2.invoke(animal);System.out.println("height: " + height);/**不用先實例化一個Animal,直接通過反射來獲得animal的class對象*/Class anotherAnimal = Class.forName("test.Animal");//Animal中g(shù)etHeight方法是私有方法,只能使用getDeclaredMethodMethod method4 = anotherAnimal.getDeclaredMethod("getHeight", null);method4.setAccessible(true);//java反射無法傳入基本類型變量,可以通過如下形式int param2 = 15;Class[] argsClass2 = new Class[] { int.class };//Animal中setHeight方法是共有方法,可以使用getMethodMethod method5 = anotherAnimal.getMethod("setHeight", argsClass2);method5.invoke(anotherAnimal.newInstance(), param2);//Animal中height變量必須聲明為static變量,這樣在重新實例化一個Animal對象后調(diào)用getHeight()才能讀到height的值// 否則重新實例化一個新的Animal對象,讀到的值為初始值int height2 = (Integer) method4.invoke(anotherAnimal.newInstance());System.out.println("height:" + height2);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();}}}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
詳見:http://blog.csdn.net/scythe666/article/details/51704809
一百九十七、C++反射機制
C++原生不支持反射機制,但是可以手動實現(xiàn):
#include <map> #include <iostream> #include <string> using namespace std;typedef void* (*PTRCreateObject)(void); class ClassFactory{ private: map<string, PTRCreateObject> m_classMap ; ClassFactory(){}; //構(gòu)造函數(shù)私有化public: void* getClassByName(string className); void registClass(string name, PTRCreateObject method) ; static ClassFactory& getInstance() ; };void* ClassFactory::getClassByName(string className){ map<string, PTRCreateObject>::const_iterator iter; iter = m_classMap.find(className) ; if ( iter == m_classMap.end() ) return NULL ; else return iter->second() ; } void ClassFactory::registClass(string name, PTRCreateObject method){ m_classMap.insert(pair<string, PTRCreateObject>(name, method)) ; } ClassFactory& ClassFactory::getInstance(){static ClassFactory sLo_factory; return sLo_factory ; } class RegisterAction{ public:RegisterAction(string className,PTRCreateObject ptrCreateFn){ClassFactory::getInstance().registClass(className,ptrCreateFn);} };#define REGISTER(className) \className* objectCreator##className(){ \return new className; \} \RegisterAction g_creatorRegister##className( \#className,(PTRCreateObject)objectCreator##className)//test class class TestClass{ public:void m_print(){cout<<"hello TestClass"<<endl;}; }; REGISTER(TestClass);int main(int argc,char* argv[]){TestClass* ptrObj=(TestClass*)ClassFactory::getInstance().getClassByName("TestClass");ptrObj->m_print(); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
詳見:http://blog.csdn.net/scythe666/article/details/51718864
一百九十八、JVM內(nèi)存模型
JVM內(nèi)存模型分區(qū)見下圖:
(1)方法區(qū):用于存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。對于習(xí)慣在HotSpot 虛擬機上開發(fā)和部署程序的開發(fā)者來說,很多人愿意把方法區(qū)稱為“永久代”(Permanent Generation),本質(zhì)上兩者并不等價。
(2)堆區(qū):Java 堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機啟動時創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內(nèi)存。由于逃逸分析等分析或優(yōu)化技術(shù),所有的對象都分配在堆上也漸漸變得不是那么“絕對”了。
Java 堆是垃圾收集器管理的主要區(qū)域,因此很多時候也被稱做“GC 堆”
(3)棧區(qū):與程序計數(shù)器一樣,Java 虛擬機棧(Java Virtual Machine Stacks)也是線程私有的,它的生命周期與線程相同。棧中主要存放一些基本類型的變量(,int, short, long, byte, float,double, boolean, char)和對象句柄。
(4)程序計數(shù)器:程序計數(shù)器(Program Counter Register)是一塊較小的內(nèi)存空間,它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。
由于Java虛擬機的多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式來實現(xiàn)的,在任何一個確定的時刻,一個處理器(對于多核處理器來說是一個內(nèi)核)只會執(zhí)行一條線程中的指令。因此,為了線程切換后能恢復(fù)到正確的執(zhí)行位置,每條線程都需要有一個獨立的程序計數(shù)器,各條線程之間的計數(shù)器互不影響,獨立存儲,我們稱這類內(nèi)存區(qū)域為“線程私有”的內(nèi)存。
(5)本地方法棧:本地方法棧(Native Method Stacks)與虛擬機棧所發(fā)揮的作用是非常相似的,其區(qū)別不過是虛擬機棧為虛擬機執(zhí)行Java 方法(也就是字節(jié)碼)服務(wù),而本地方法棧則是為虛擬機使用到的Native 方法服務(wù)。
詳見:http://blog.csdn.net/scythe666/article/details/51700142
一百久十久、阻塞VS非阻塞,同步VS異步
同步異步是針對整個IO的過程,阻塞非阻塞是針對單次操作
用很貼切的比喻來說明同步異步:
回調(diào)的比喻:A委托B做事,但是B現(xiàn)在忙,于是A告訴B如何去做,A就回去了,B做完手頭事情后把A的事情做完了
通知的比喻:水壺?zé)?#xff0c;如果一直看著水壺,是否冒煙,就是同步
如果燒好水壺響了才去管就是異步。
同步異步是一種動作,阻塞和非阻塞是一種狀態(tài)
1、同步和異步是針對應(yīng)用程序和內(nèi)核的交互而言的。
(1)同步:用戶進程觸發(fā)IO操作并等待或者輪詢的去查看IO操作是否就緒;
(2)異步:用戶進程觸發(fā)IO操作以后便開始做自己的事情,而當(dāng)IO操作已經(jīng)完成的時候會得到IO完成的通知(異步的特點就是通知)。
2、阻塞和非阻塞是針對于進程在訪問數(shù)據(jù)的時候,根據(jù)IO操作的就緒狀態(tài)來采取的不同方式,說白了是一種讀取或者寫入操作函數(shù)的實現(xiàn)方式
(1)阻塞方式下讀取或者寫入函數(shù)將一直等待
(2)非阻塞方式下,讀取或者寫入函數(shù)會立即返回一個狀態(tài)值。
一般來說I/O模型可以分為:同步阻塞,同步非阻塞,異步阻塞,異步非阻塞IO
(1)同步阻塞IO:
在此種方式下,用戶進程在發(fā)起一個IO操作以后,必須等待IO操作的完成,只有當(dāng)真正完成了IO操作以后,用戶進程才能運行。JAVA傳統(tǒng)的IO模型屬于此種方式!
(2)同步非阻塞IO:
在此種方式下,用戶進程發(fā)起一個IO操作以后邊可返回做其它事情,但是用戶進程需要時不時的詢問IO操作是否就緒,這就要求用戶進程不停的去詢問,從而引入不必要的CPU資源浪費。其中目前JAVA的NIO就屬于同步非阻塞IO。
(3)異步阻塞IO:
此種方式下是指應(yīng)用發(fā)起一個IO操作以后,不等待內(nèi)核IO操作的完成,等內(nèi)核完成IO操作以后會通知應(yīng)用程序,這其實就是同步和異步最關(guān)鍵的區(qū)別,同步必須等待或者主動的去詢問IO是否完成,那么為什么說是阻塞的呢?因為此時是通過select系統(tǒng)調(diào)用來完成的,而select函數(shù)本身的實現(xiàn)方式是阻塞的,而采用select函數(shù)有個好處就是它可以同時監(jiān)聽多個文件句柄(如果從UNP的角度看,select屬于同步操作。因為select之后,進程還需要讀寫數(shù)據(jù)),從而提高系統(tǒng)的并發(fā)性!
(4)異步非阻塞IO:
在此種模式下,用戶進程只需要發(fā)起一個IO操作然后立即返回,等IO操作真正的完成以后,應(yīng)用程序會得到IO操作完成的通知,此時用戶進程只需要對數(shù)據(jù)進行處理就好了,不需要進行實際的IO讀寫操作,因為真正的IO讀取或者寫入操作已經(jīng)由內(nèi)核完成了。目前Java中還沒有支持此種IO模型。
(其實阻塞與非阻塞都可以理解為同步范疇下才有的概念,對于異步,就不會再去分阻塞非阻塞。對于用戶進程,接到異步通知后,就直接操作進程用戶態(tài)空間里的數(shù)據(jù)好了。)
詳見:http://blog.csdn.net/scythe666/article/details/51701360
二百、高性能IO設(shè)計的Reactor和Proactor模式
首先來看看Reactor模式,Reactor模式應(yīng)用于同步I/O的場景。我們分別以讀操作和寫操作為例來看看Reactor中的具體步驟:
讀取操作:
1. 應(yīng)用程序注冊讀就緒事件和相關(guān)聯(lián)的事件處理器
2. 事件分離器等待事件的發(fā)生
3. 當(dāng)發(fā)生讀就緒事件的時候,事件分離器調(diào)用第一步注冊的事件處理器
4. 事件處理器首先執(zhí)行實際的讀取操作,然后根據(jù)讀取到的內(nèi)容進行進一步的處理
寫入操作類似于讀取操作,只不過第一步注冊的是寫就緒事件。
下面我們來看看Proactor模式中讀取操作和寫入操作的過程:
讀取操作:
1. 應(yīng)用程序初始化一個異步讀取操作,然后注冊相應(yīng)的事件處理器,此時事件處理器不關(guān)注讀取就緒事件,而是關(guān)注讀取完成事件,這是區(qū)別于Reactor的關(guān)鍵。
2. 事件分離器等待讀取操作完成事件
3. 在事件分離器等待讀取操作完成的時候,操作系統(tǒng)調(diào)用內(nèi)核線程完成讀取操作(異步IO都是操作系統(tǒng)負(fù)責(zé)將數(shù)據(jù)讀寫到應(yīng)用傳遞進來的緩沖區(qū)供應(yīng)用程序操作,操作系統(tǒng)扮演了重要角色),并將讀取的內(nèi)容放入用戶傳遞過來的緩存區(qū)中。這也是區(qū)別于Reactor的一點,Proactor中,應(yīng)用程序需要傳遞緩存區(qū)。
4. 事件分離器捕獲到讀取完成事件后,激活應(yīng)用程序注冊的事件處理器,事件處理器直接從緩存區(qū)讀取數(shù)據(jù),而不需要進行實際的讀取操作。
Proactor中寫入操作和讀取操作,只不過感興趣的事件是寫入完成事件。
從上面可以看出,Reactor和Proactor模式的主要區(qū)別就是真正的讀取和寫入操作是有誰來完成的,Reactor中需要應(yīng)用程序自己讀取或者寫入數(shù)據(jù),而Proactor模式中,應(yīng)用程序不需要進行實際的讀寫過程,它只需要從緩存區(qū)讀取或者寫入即可,操作系統(tǒng)會讀取緩存區(qū)或者寫入緩存區(qū)到真正的IO設(shè)備.
綜上所述,同步和異步是相對于應(yīng)用和內(nèi)核的交互方式而言的,同步 需要主動去詢問,而異步的時候內(nèi)核在IO事件發(fā)生的時候通知應(yīng)用程序,而阻塞和非阻塞僅僅是系統(tǒng)在調(diào)用系統(tǒng)調(diào)用的時候函數(shù)的實現(xiàn)方式而已。
詳見:https://segmentfault.com/a/1190000002715832
二百零一、秒懂樂觀鎖和悲觀鎖
悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數(shù)據(jù)的時候都認(rèn)為別人會修改,所以每次在拿數(shù)據(jù)的時候都會上鎖,這樣別人想拿這個數(shù)據(jù)就會block直到它拿到鎖。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數(shù)據(jù)的時候都認(rèn)為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數(shù)據(jù),可以使用版本號等機制。樂觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量,像數(shù)據(jù)庫如果提供類似于write_condition機制的其實都是提供的樂觀鎖。
兩種鎖各有優(yōu)缺點,不可認(rèn)為一種好于另一種,像樂觀鎖適用于寫比較少的情況下,即沖突真的很少發(fā)生的時候,這樣可以省去了鎖的開銷,加大了系統(tǒng)的整個吞吐量。但如果經(jīng)常產(chǎn)生沖突,上層應(yīng)用會不斷的進行retry,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適。
原文:http://blog.csdn.net/hongchangfirst/article/details/26004335
二百零二、序列化和反序列化
1、什么是序列化
序列化(Serialization)是將對象的狀態(tài)信息轉(zhuǎn)換為可以存儲或傳輸?shù)男问降倪^程。在序列化期間,對象將其當(dāng)前狀態(tài)寫入到臨時或持久性存儲區(qū)。之后可以通過從存儲區(qū)中讀取或反序列化對象的狀態(tài),重新創(chuàng)建該對象。
也可以理解為:將對象轉(zhuǎn)換為容易傳輸?shù)母袷降倪^程。例如,可以序列化一個對象,然后使用 HTTP 通過 Internet 在客戶端和服務(wù)器之間傳輸該對象。在另一端,反序列化將從該流重新構(gòu)造對象。
2、Java序列化的實現(xiàn)
序列化的實現(xiàn):將需要被序列化的類實現(xiàn)Serializable接口,該接口沒有需要實現(xiàn)的方法,implements Serializable只是為了標(biāo)注該對象是可被序列化的,然后使用一個輸出流(如:FileOutputStream)來構(gòu)造一個ObjectOutputStream(對象流)對象,接著,使用ObjectOutputStream對象的writeObject(Object obj)方法就可以將參數(shù)為obj的對象寫出(即保存其狀態(tài)),要恢復(fù)的話則用輸入流
3、什么時候使用序列化
(1)對象序列化可以實現(xiàn)分布式對象。主要應(yīng)用例如:RMI要利用對象序列化運行遠(yuǎn)程主機上的服務(wù),就像在本地機上運行對象時一樣。
(2)java對象序列化不僅保留一個對象的數(shù)據(jù),而且遞歸保存對象引用的每個對象的數(shù)據(jù)。可以將整個對象層次寫入字節(jié)流中,可以保存在文件中或在網(wǎng)絡(luò)連接上傳遞。利用對象序列化可以進行對象的”深復(fù)制”,即復(fù)制對象本身及引用的對象本身。序列化一個對象可能得到整個對象序列。
二百零三、關(guān)于RPC
首先了解什么叫RPC,為什么要RPC,RPC是指遠(yuǎn)程過程調(diào)用,也就是說兩臺服務(wù)器A,B,一個應(yīng)用部署在A服務(wù)器上,想要調(diào)用B服務(wù)器上應(yīng)用提供的函數(shù)/方法,由于不在一個內(nèi)存空間,不能直接調(diào)用,需要通過網(wǎng)絡(luò)來表達(dá)調(diào)用的語義和傳達(dá)調(diào)用的數(shù)據(jù)。
詳見:http://blog.csdn.net/scythe666/article/details/51719408
二百零四、非阻塞同步算法與CAS(Compare and Swap)無鎖算法
要實現(xiàn)無鎖(lock-free)的非阻塞算法有多種實現(xiàn)方法,其中CAS(比較與交換,Compare and swap)是一種有名的無鎖算法。CAS, CPU指令,在大多數(shù)處理器架構(gòu),包括IA32、Space中采用的都是CAS指令,CAS的語義是“我認(rèn)為V的值應(yīng)該為A,如果是,那么將V的值更新為B,否則不修改并告訴V的值實際為多少”,CAS是項樂觀鎖技術(shù),當(dāng)多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程并不會被掛起,而是被告知這次競爭中失敗,并可以再次嘗試。CAS有3個操作數(shù),內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的新值B。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時,將內(nèi)存值V修改為B,否則什么都不做。CAS無鎖算法的C實現(xiàn)如下:
int compare_and_swap (int* reg, int oldval, int newval) {ATOMIC();int old_reg_val = *reg;if (old_reg_val == oldval) *reg = newval;END_ATOMIC();return old_reg_val; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
CAS比較與交換的偽代碼可以表示為:
do{ 備份舊數(shù)據(jù); 基于舊數(shù)據(jù)構(gòu)造新數(shù)據(jù); }while(!CAS( 內(nèi)存地址,備份的舊數(shù)據(jù),新數(shù)據(jù) ))- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
(上圖的解釋:CPU去更新一個值,但如果想改的值不再是原來的值,操作就失敗,因為很明顯,有其它操作先改變了這個值。)
就是指當(dāng)兩者進行比較時,如果相等,則證明共享數(shù)據(jù)沒有被修改,替換成新值,然后繼續(xù)往下運行;如果不相等,說明共享數(shù)據(jù)已經(jīng)被修改,放棄已經(jīng)所做的操作,然后重新執(zhí)行剛才的操作。容易看出 CAS 操作是基于共享數(shù)據(jù)不會被修改的假設(shè),采用了類似于數(shù)據(jù)庫的 commit-retry 的模式。當(dāng)同步?jīng)_突出現(xiàn)的機會很少時,這種假設(shè)能帶來較大的性能提升。
詳見:http://www.cnblogs.com/Mainz/p/3546347.html?utm_source=tuicool&utm_medium=referral
二百零五、spring框架學(xué)習(xí)
基本學(xué)習(xí)Java,spring是必學(xué)的,這個框架是Java集大成者,活用各種Java特性和設(shè)計模式,實在是牛逼的不行,如果可以的話最好讀源碼。
詳見:http://blog.csdn.net/scythe666/article/details/51706041
二百零六、static塊的本質(zhì)
Java靜態(tài)變量的初始化
詳見:http://blog.csdn.net/darxin/article/details/5293427
二百零七、Java的override注解寫與不寫
/*Java 中的覆蓋@Override注解 寫與不寫的一點點理解一般來說,寫與不寫沒什么區(qū)別,JVM可以自識別寫的情況下:即說明子類要覆蓋基類的方法,基類必須存在方法(控制類型public,protected,返回值,參數(shù)列表類型)與子類方法完成一致的方法,否則會報錯(找不到被Override的方法)。在不寫@Override注解的情況下,當(dāng)基類存在與子類各種條件都符合的方法是即實現(xiàn)覆蓋;如果條件不符合時,則是當(dāng)成新定義的方法使用。所以如果想覆蓋基類方法時,最好還是寫上@Override注解,這樣有利于編譯器幫助檢查錯誤 */public class OverrideTest extends Test{@Override//此處寫與不寫都能覆蓋基類的t(String)方法public void t(String s){System.out.println("OverrideTest.t(String):" + s);}//此處不能寫@Override注解,因為方法參數(shù)類型與基類的t2方法的參數(shù)類型不同//所以此處只能新定義了一個t2(float)方法,并不能實現(xiàn)覆蓋public void t2(float f){System.out.println("OverrideTest.t2(float):" + f);}public static void main(String[] args){OverrideTest ot = new OverrideTest();ot.t("china34420");ot.t2(1.0f);ot.t2(1);ot.t3();} } /*輸出:OverrideTest.t(String):china34420OverrideTest.t2(float):1.0Test.t2(int):1OverrideTest.t(String):override */class Test{public void t(String s){System.out.println("Test.t(String):" + s);}public void t2(int i){System.out.println("Test.t2(int):" + i); }public void t3(){t("override"); } }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
二百零八、進程VS端口
一個進程可以監(jiān)聽多個端口
nginx可以配置多個server,每個server監(jiān)聽不同的端口.
PHP-fpm可以配置多個pool,每個pool監(jiān)聽不同的端口.
httpd可以用Listen監(jiān)聽多個端口(Listen監(jiān)聽了這些端口,VirtualHost里才能使用這些端口)
二百零九、有用過Linux中的epoll嗎?它的作用是什么?
答:
epoll是linux內(nèi)核為處理大批量文件描述符而作了改進的poll,是Linux下多路復(fù)用IO接口select/poll的增強版本,它能顯著提高程序在大量并發(fā)連接中只有少量活躍的情況下的系統(tǒng)CPU利用率。
二百一十、epoll和select的區(qū)別在哪,或者說優(yōu)勢在哪?
答:
(1)epoll除了提供select/poll那種IO事件的水平觸發(fā)(Level Triggered)外,還提供了邊緣觸發(fā)(Edge Triggered);
(2)select的句柄數(shù)目受限,在linux/posix_types.h頭文件有這樣的聲明:#define __FD_SETSIZE 1024,表示select最多同時監(jiān)聽1024個fd。而epoll沒有,epoll的最大并發(fā)的連接數(shù)的理論值無上限,但由于實際內(nèi)存資源有限,實際并發(fā)的連接數(shù)受到資源的限制和最大的打開文件句柄數(shù)目的限制;
(3)epoll的最大好處是不會隨著FD的數(shù)目增長而降低效率,在selec中采用輪詢處理,其中的數(shù)據(jù)結(jié)構(gòu)類似一個數(shù)組的數(shù)據(jù)結(jié)構(gòu),而epoll 是維護一個隊列,直接看隊列是不是空就可以了。
(4)使用mmap加速內(nèi)核與用戶空間的消息傳遞。無論是select,poll還是epoll都需要內(nèi)核把FD消息通知給用戶空間,如何避免不必要的內(nèi)存拷貝就很重要,在這點上,epoll是通過內(nèi)核于用戶空間mmap同一塊內(nèi)存實現(xiàn)的。
此外,epoll創(chuàng)建時傳入的參數(shù)是什么?
epoll對象可通過int epoll_create(int size)來創(chuàng)建一個epoll的實例,size用來告訴內(nèi)核這個監(jiān)聽的數(shù)目一共有多大。這個參數(shù)不同于select()中的第一個參數(shù),給出最大監(jiān)聽的fd+1的值。需要注意的是,當(dāng)創(chuàng)建好epoll句柄后,它就是會占用一個fd值。所以在使用完epoll后,必須調(diào)用close()關(guān)閉,否則可能導(dǎo)致fd被耗盡。但是自從linux2.6.8之后,size參數(shù)是被忽略的。
此外,創(chuàng)建epoll實例,還可以通過int epoll_create1(int flags)。
這個函數(shù)是在linux 2.6.27中加入的,其實它和epoll_create差不多,不同的是epoll_create1函數(shù)的參數(shù)是flags,當(dāng)flag是0時,表示和epoll_create函數(shù)完全一樣,不需要size的提示了。
當(dāng)flag = EPOLL_CLOEXEC,創(chuàng)建的epfd會設(shè)置FD_CLOEXEC;
當(dāng)flag = EPOLL_NONBLOCK,創(chuàng)建的epfd會設(shè)置為非阻塞。
一般用法都是使用EPOLL_CLOEXEC。關(guān)于EPOLL_CLOEXEC,網(wǎng)上資料說明是對epfd的一個標(biāo)識說明,用來設(shè)置文件close-on-exec狀態(tài)的。當(dāng)close-on-exec狀態(tài)為0時,調(diào)用exec時,fd不會被關(guān)閉;狀態(tài)非零時則會被關(guān)閉,這樣做可以防止fd泄露給執(zhí)行exec后的進程。關(guān)于exec的用法,大家可以去自己查閱下,或者直接man exec。
二百一十二、寫出一個程序,讓CPU成正弦曲線
http://www.cnblogs.com/matrix-r/p/3246838.html
二百一十三、什么是編程語言的自舉?
就是自己的編譯器可以自行編譯自己的編譯器。
實現(xiàn)方法就是這個編譯器的作者用這個語言的一些特性來編寫編譯器并在該編譯器中支持這些自己使用到的特性。
首先,第一個編譯器肯定是用別的語言寫的(不論是C還是Go還是Lisp還是Python),后面的版本才能談及自舉。
至于先有雞還是先有蛋,我可以舉個這樣的不太恰當(dāng)?shù)睦?#xff1a;比如我寫了一個可以自舉的C編譯器叫作mycc,不論是編譯器本身的執(zhí)行效率還是生成的代碼的質(zhì)量都遠(yuǎn)遠(yuǎn)好于gcc(本故事純屬虛構(gòu)),但我用的都是標(biāo)準(zhǔn)的C寫的,那么我可以就直接用gcc編譯mycc的源碼,得到一份可以生成高質(zhì)量代碼但本身執(zhí)行效率低下的mycc,然后當(dāng)然如果我再用這個生成的mycc編譯mycc的源碼得到新的一份mycc,新的這份不光會產(chǎn)生和原來那份同等高質(zhì)量的代碼,而且還能擁有比先前版本更高的執(zhí)行效率(因為前一份是gcc的編譯產(chǎn)物,后一份是mycc的編譯產(chǎn)物,而mycc生成的代碼質(zhì)量要遠(yuǎn)好于gcc的)。故事雖然是虛構(gòu)的,但是道理差不多就是這么個道理。這也就是為什么如果從源碼編譯安裝新版本的gcc的話,往往會“編譯——安裝”兩到三遍的原因。
二百一十四、內(nèi)存為什么要分頁
假設(shè)內(nèi)存是連續(xù)分配的(也就是程序在物理內(nèi)存上是連續(xù)的)
1.進程A進來,向os申請了200的內(nèi)存空間,于是os把0~199分配給A
2.進程B進來,向os申請了5的內(nèi)存空間,os把200~204分配給它
3.進程C進來,向os申請了100的內(nèi)存空間,os把205~304分配給它
4.這個時候進程B運行完了,把200~204還給os
但是很長時間以后,只要系統(tǒng)中的出現(xiàn)的進程的大小>5的話,200~204這段空間都不會被分配出去(只要A和C不退出)。
過了一段更長的時間,內(nèi)存中就會出現(xiàn)許許多多200~204這樣不能被利用的碎片……
而分頁機制讓程序可以在邏輯上連續(xù)、物理上離散。也就是說在一段連續(xù)的物理內(nèi)存上,可能0~4(這個值取決于頁面的大小)屬于A,而5~9屬于B,10~14屬于C,從而保證任何一個“內(nèi)存片段”都可以被分配出去。
二百一十五、高級語言為什么不直接編譯成機器碼,而編譯成匯編代碼
1.一般的編譯器,是先將高級語言轉(zhuǎn)換成匯編語言(中間代碼),然后在匯編的基礎(chǔ)上優(yōu)化生成OBJ目標(biāo)代碼,最后Link成可執(zhí)行文件。
2.Que:高級語言為什么不直接編譯成機器碼,而編譯成匯編代碼?
ACK:1)其中有一個好處是方便優(yōu)化,因為,編譯器也是工具,也是機器,畢竟是機器生成的程序,不可以非常 完美的,而匯編是機器指令的助記符,一個匯編指令就對應(yīng)一條機器指令(特殊指令除外)調(diào)試起來肯定會比 機器指令方便的方便,這樣優(yōu)化起來也方便。
2)高級語言只需要編譯成匯編代碼就可以了,匯編代碼到機器碼的轉(zhuǎn)換是由硬件實現(xiàn)即可,有必要用軟件實 現(xiàn)這樣分層可以有效地減弱編譯器編寫的復(fù)雜性,提高了效率.就像網(wǎng)絡(luò)通訊的實現(xiàn)需要分成很多層一樣,主要 目的就是為了從人腦可分析的粒度來減弱復(fù)雜性.
3)如果把高級語言的源代碼直接編譯成機器碼的話,那要做高級語言到機器碼之間的映射,如果這樣做的 話,每個寫編譯器的都必須熟練機器碼。這個不是在做重復(fù)勞動么。
二百一十六、全雙工 VS 半雙工
全雙工(Full Duplex)是指在發(fā)送數(shù)據(jù)的同時也能夠接收數(shù)據(jù),兩者同步進行,這好像我們平時打電話一樣,說話的同時也能夠聽到對方的聲音。目前的網(wǎng)卡一般都支持全雙工。
半雙工(Half Duplex),所謂半雙工就是指一個時間段內(nèi)只有一個動作發(fā)生,舉個簡單例子,一條窄窄的馬路,同時只能有一輛車通過,當(dāng)目前有兩量車對開,這種情況下就只能一輛先過,等到頭兒后另一輛再開,這個例子就形象的說明了半雙工的原理。早期的對講機、以及早期集線器等設(shè)備都是基于半雙工的產(chǎn)品。隨著技術(shù)的不斷進步,半雙工會逐漸退出歷史舞臺.
單工通信是指通信線路上的數(shù)據(jù)按單一方向傳送
TCP/UDP都是全雙工的
二百一十七、MSS
在有些書中,將它看作可“協(xié)商”選項。它并不是任何條件下都可協(xié)商。當(dāng)建立一個連
接時,每一方都有用于通告它期望接收的MSS選項(MSS選項只能出現(xiàn)在SYN報文段中)。如果一方不接收來自另一方的MSS值,則MSS就定為默認(rèn)值536字節(jié)(這個默認(rèn)值允許20字節(jié)的IP首部和20字節(jié)的TCP首部以適合576字節(jié)IP數(shù)據(jù)報)。
一般說來,如果沒有分段發(fā)生,MSS還是越大越好(這也并不總是正確,參見圖24-3和
圖24-4中的例子)。報文段越大允許每個報文段傳送的數(shù)據(jù)就越多,相對IP和TCP首部有更高的網(wǎng)絡(luò)利用率。當(dāng)TCP發(fā)送一個SYN時,或者是因為一個本地應(yīng)用進程想發(fā)起一個連接,或
者是因為另一端的主機收到了一個連接請求,它能將MSS值設(shè)置為外出接口上的MTU長度減
去固定的IP首部和TCP首部長度。對于一個以太網(wǎng),MSS值可達(dá)1460字節(jié)。使用IEEE802.3的
封裝(參見2.2節(jié)),它的MSS可達(dá)1452字節(jié)。
二百一十八、Linux配色修改
http://blog.csdn.net/hexinzheng19780209/article/details/45539431
https://www.zhihu.com/question/20110072
二百一十九、網(wǎng)絡(luò)傳輸包大小是否固定問題
IP、TCP、UDP報頭詳見:http://blog.csdn.net/Scythe666/article/details/51909186
TCP/IP協(xié)議定義了一個在因特網(wǎng)上傳輸?shù)陌?#xff0c;稱為IP數(shù)據(jù)包,而IP數(shù)據(jù)報(IP Datagram)是個比較抽象的內(nèi)容,是對數(shù)據(jù)包的結(jié)構(gòu)進行分析。 由首部和數(shù)據(jù)兩部分組成,其格式如圖所示。首部的前一部分是固定長度,共20字節(jié),是所有IP數(shù)據(jù)報必須具有的。在首部的固定部分的后面是一些可選字段,其長度是可變的。首部中的源地址和目的地址都是IP協(xié)議地址。
總長度指首部和數(shù)據(jù)之和的長度,單位為字節(jié)。總長度字段為16位,因此數(shù)據(jù)報的最大長度為2^16-1=65535字節(jié)。
在IP層下面的每一種數(shù)據(jù)鏈路層都有自己的幀格式,其中包括幀格式中的數(shù)據(jù)字段的最大長度,這稱為最大傳送單元MTU(Maximum Transfer Unit)。當(dāng)一個數(shù)據(jù)報封裝成鏈路層的幀時,此數(shù)據(jù)報的總長度(即首部加上數(shù)據(jù)部分)不能超過下面的數(shù)據(jù)鏈路層的MTU值。
深入淺出好文:http://www.cnblogs.com/maowang1991/archive/2013/04/15/3022955.html
二百二十、以太網(wǎng) VS 令牌環(huán)網(wǎng)
以太網(wǎng)是這樣通信的,每臺電腦位于同一個主干中都可以向主干線路中發(fā)信息串。假如a吧,它先監(jiān)聽主干線路上有沒有人在發(fā)信息,如果有它就等一會兒,在它發(fā)現(xiàn)沒有人發(fā)言后它將發(fā)言,但這時有可能另一臺電腦也和它同時發(fā)言(想象一下在課堂上兩個學(xué)生向老師同時提問),這樣它們會同時停止發(fā)言,并在等待了一個隨機時間后繼續(xù)發(fā)言,當(dāng)然它們的隨機時間是不同的,并且在再次發(fā)言前仍需監(jiān)聽主干上是否有其它主機在發(fā)言。其它的電腦讀取數(shù)據(jù)包,檢查mac地址和ip地址乃至端口號看是不是發(fā)給自已的,如果不是便丟棄。它的mac 算法是csma/cd算法
令牌環(huán)網(wǎng)的結(jié)構(gòu)是組成一個環(huán)形,環(huán)形的一圈是主機,主機中存在一個令牌,由一號機向下傳,每個主機只有在自已有令牌時才能向主線路中發(fā)數(shù)據(jù)。
二百二十一、MSS與MTU的關(guān)系(TCP分段和IP分割和重組報文)
MSS 指的是 TCP payload 的長度,MSS讓主機限制另一端發(fā)送數(shù)據(jù)報的長度。加上主機也能控制它發(fā)送數(shù)據(jù)報的長度,這將使以較小 MTU連接到一個網(wǎng)絡(luò)上的主機避免分段。
為什么 L3 有 MTU 后 L4 還要 MSS 呢?
MTU 和 MSS 的功能其實基本一致, 都可以根據(jù)對應(yīng)的包大小進行分片, 但實現(xiàn)的效果卻不太一樣.
L3 (IP) 提供的是一個不可靠的傳輸方式, 如果任何一個包在傳輸?shù)倪^程中丟失了, L3 是無法發(fā)現(xiàn)的, 需要靠上層應(yīng)用來保證. 就是說如果一個大 IP 包分片后傳輸, 丟了任何一個部分都無法組合出完整的 IP 包, 即是上層應(yīng)用發(fā)現(xiàn)了傳輸失敗, 也無法做到僅重傳丟失的分片, 只能把 IP 包整個重傳. 那 IP 包越大的話重傳的代價也就越高.
L4 (TCP) 提供的是一個可靠的傳輸方式, 與 L3 不同的是, TCP 自身實現(xiàn)了重傳機制, 丟了任何一片數(shù)據(jù)報都能單獨重傳, 所以 TCP 為了高效傳輸, 是需要極力避免被 L3 分片的, 所以就有了 MSS 標(biāo)志, 并且 MSS 的值就是根據(jù) MTU 計算得出, 既避免了 L3 上的分片, 又保證的最大的傳輸效率.
http://networkengineering.stackexchange.com/questions/8288/difference-between-mss-and-mtu
http://blog.apnic.net/2014/12/15/ip-mtu-and-tcp-mss-missmatch-an-evil-for-network-performance/
二百二十二、都說路由器可以設(shè)置MTU,為什么是第三層設(shè)備設(shè)置呢
最大傳輸單元,即物理接口(數(shù)據(jù)鏈路層)提供給其上層(通常是IP層)最大一次傳輸數(shù)據(jù)的大小
二百二十三、不是說TCP報頭沒有長度限制嗎,MSS又是什么
詳見兩篇超好的知乎文章,車小胖:https://zhuanlan.zhihu.com/p/21268782
https://www.zhihu.com/question/48454744
二百二十四、TCP/IP 協(xié)議棧為什么有粘包問題,如何解決
如何解決
(1)加入分隔符字段,自己定義結(jié)束標(biāo)志
(2)加入payload長度字段(注意是加入一個字段),比如http應(yīng)用層協(xié)議就是這樣做的
二百二十五、網(wǎng)絡(luò)傳輸?shù)臅r候有大小端的問題,如何解決
二百二十六、為什么TCP要三次握手
二百二十七、TCP四次揮手,為什么要等待2MSL
什么是MSL
二百二十八、TCP有限狀態(tài)機
三次握手、四次揮手中發(fā)起端(左邊)是粗實線狀態(tài)
右邊是粗虛線狀態(tài)
二百二十九、裝飾器模式與代理的區(qū)別
實現(xiàn)手段差不多,但是目的有微妙的差別
裝飾器模式:基于原有功能的加強,主要是功能的差別,而且功能只能加強,例子Java IO流
代理:由另一個類去執(zhí)行,主要是執(zhí)行者的差別,當(dāng)然功能可以加強可以減弱
二百三十、Linux系統(tǒng)uid
用戶的UID大于500的都是非系統(tǒng)賬號,500以下的都為系統(tǒng)保留的賬號,比如root賬號,至高權(quán)限的賬號的UID為0,我們創(chuàng)建用戶的時候默認(rèn)的賬號的UID都是大于500,如果你要指定賬號的UID可以使用-u這個參數(shù)來指定。其它沒什么大的意義。
二百三十一、什么是驅(qū)動
驅(qū)動程序雖然在你打開電腦進入系統(tǒng)之后就不斷地在為你服務(wù)了,但是它不像電腦硬件那樣觸手可及,也不像操作系統(tǒng)那樣一目了然,更不像游戲或者多媒體應(yīng)用那樣引人注目,它只是默默無聞的在后臺做著自己該做的事情,因此總是被很多朋友忽略。
那么驅(qū)動是什么呢?驅(qū)動的英文就是Driver,簡單的說來驅(qū)動程序就是用來向操作系統(tǒng)提供一個訪問、使用硬件設(shè)備的接口,實現(xiàn)操作系統(tǒng)和系統(tǒng)中所有的硬件設(shè)備的之間的通信程序,它能告訴系統(tǒng)硬件設(shè)備所包含的功能,并且在軟件系統(tǒng)要實現(xiàn)某個功能時,調(diào)動硬件并使硬件用最有效的方式來完成它。
說的形象一點,驅(qū)動程序就是軟件與硬件之間的“傳令兵”,這個環(huán)節(jié)可是大大的重要,一旦出現(xiàn)了問題,那么軟件提出的要求就要無人響應(yīng),而硬件卻空有一身力氣但無從發(fā)揮,那種狀況下朋友們會發(fā)現(xiàn)自己那本來性能強大且多姿多彩的電腦竟然如同一洼死水,什么都做不來了。因此有人說驅(qū)動是硬件的靈魂,可毫不為過。
二百三十二、CPU使用分時間片,會不會很浪費資源
時間片輪轉(zhuǎn)調(diào)度中特別需要關(guān)注的是時間片的長度。從一個進程切換到另一個進程是需要一定時間的–保存和裝入寄存器值及內(nèi)存映像,更新各種表格和隊列等。假如進程切換(process switch) - 有時稱為上下文切換(context switch),需要5毫秒,再假設(shè)時間片設(shè)為20毫秒,則在做完20毫秒有用的工作之后,CPU將花費5毫秒來進行進程切換。CPU時間的20%被浪費在了管理開銷上。
為了提高CPU效率,我們可以將時間片設(shè)為500毫秒。這時浪費的時間只有1%。但考慮在一個分時系統(tǒng)中,如果有十個交互用戶幾乎同時按下回車鍵,將發(fā)生什么情況?假設(shè)所有其他進程都用足它們的時間片的話,最后一個不幸的進程不得不等待5秒鐘才獲得運行機會。多數(shù)用戶無法忍受一條簡短命令要5秒鐘才能做出響應(yīng)。同樣的問題在一臺支持多道程序的個人計算機上也會發(fā)生。
結(jié)論可以歸結(jié)如下:時間片設(shè)得太短會導(dǎo)致過多的進程切換,降低了CPU效率;而設(shè)得太長又可能引起對短的交互請求的響應(yīng)變差。
二百三十三、主分區(qū)、擴展分區(qū)、邏輯分區(qū)
一個硬盤的主分區(qū)也就是包含操作系統(tǒng)啟動所必需的文件和數(shù)據(jù)的硬盤分區(qū),要在硬盤上安裝操作系統(tǒng),則該硬盤必須得有一個主分區(qū)。
擴展分區(qū)也就是除主分區(qū)外的分區(qū),但它不能直接使用,必須再將它劃分為若干個邏輯分區(qū)才行。邏輯分區(qū)也就是我們平常在操作系統(tǒng)中所看到的D、E、F等盤。
不管使用哪種分區(qū)軟件,我們在給新硬盤上建立分區(qū)時都要遵循以下的順序:建立主分區(qū)→建立擴展分區(qū)→建立邏輯分區(qū)→激活主分區(qū)→格式化所有分區(qū)。
總結(jié)
以上是生活随笔為你收集整理的[置顶] C/C++超级大火锅的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 组件的 props
- 下一篇: red6.4 mysql_rhel6.4