程序員的自我修養(yǎng)--讀書(shū)筆記
1:注意不要反回指向棧內(nèi)存的指針或引用,因?yàn)樵诤瘮?shù)返回時(shí)改內(nèi)存已經(jīng)被銷(xiāo)毀了
2:C/C++沒(méi)有辦法知道指針?biāo)傅膬?nèi)存容量大小 當(dāng)數(shù)組作為參數(shù)傳遞時(shí),數(shù)組將退化成相同類(lèi)型的指針 不要指望要指針參數(shù)去申請(qǐng)動(dòng)態(tài)內(nèi)存,因?yàn)楹瘮?shù)會(huì)為產(chǎn)生一個(gè)臨時(shí)變量指向參數(shù)的內(nèi)存,當(dāng)函數(shù)內(nèi)分配內(nèi)存時(shí),將內(nèi)存的地址賦給了臨時(shí)參數(shù),而沒(méi)有給實(shí)參賦值,所有實(shí)參沒(méi)有發(fā)生任何變化,應(yīng)該修改的是指針?biāo)傅膬?nèi)容,而不是修改指針的指向,所有可以用指向指針的指針
3:重載和內(nèi)聯(lián)機(jī)制既可用于全局函數(shù)也可用于類(lèi)的成員函數(shù),const和virtual機(jī)制即用于類(lèi)的成員函數(shù)
4:在繼承關(guān)系中,非虛方法:調(diào)用指針類(lèi)型的方法;虛方法:調(diào)用指針?biāo)傅膶?duì)象類(lèi)型的方法 非虛方法和默認(rèn)參數(shù)都是靜態(tài)綁定,在繼承關(guān)系中只跟指針類(lèi)型有關(guān),跟指針?biāo)傅膶?duì)象的實(shí)際類(lèi)型無(wú)關(guān)
5:互相引用的兩個(gè)類(lèi),兩個(gè)類(lèi)最好聲明在同一個(gè)頭文件中,定義可以放在同一個(gè)或兩個(gè)的文件中;這樣即解決了互相引用的問(wèn)題,同時(shí)解決了在一個(gè)類(lèi)中不能正確delete另一個(gè)類(lèi)
6:處理#include預(yù)編譯指令,將被包含的文件插入到預(yù)編譯指令的位置,這個(gè)過(guò)程是遞歸進(jìn)行的
7:C語(yǔ)言的編譯后執(zhí)行語(yǔ)句都編譯成機(jī)器代碼,保存在.text段
.data:已初始化的全局變量和局部靜態(tài)變量
.bss:為未初始化的全局變量和局部靜態(tài)變量預(yù)留位置而已(不占磁盤(pán)空間,運(yùn)行時(shí)當(dāng)然是占空間的)
.rodata:存放只讀數(shù)據(jù),const修飾的變量,常量字符串;(傳說(shuō)中的字符串池,暈,別說(shuō)得這么高級(jí),只不過(guò)是在編譯的時(shí)候就分配好了內(nèi)存)
8:程序源代碼被編譯后主要分成兩種段:程序指令和程序數(shù)據(jù) 分開(kāi)的好處:
1:數(shù)據(jù)和指令分別被映射到兩個(gè)虛存區(qū)域,數(shù)據(jù)可讀寫(xiě),指令只讀;
2:CPU的高級(jí)緩存分為數(shù)據(jù)緩存和指令緩存
3:當(dāng)程序有多個(gè)副本時(shí),可以共享指令,有各自的數(shù)據(jù),資源(圖片)共享
9:可執(zhí)行文件中的代碼段和數(shù)據(jù)段都是由輸入的目標(biāo)文件中相應(yīng)的段合并而來(lái)的
10:作用域:全局變量不管定義在哪里(.h或.cpp)整個(gè)解決方案都可見(jiàn),定義在頭文件中的靜態(tài)全局變量整個(gè)解決方案都可以見(jiàn),定義在實(shí)現(xiàn)文件(.cpp)中的靜態(tài)變量只有這個(gè)文件可見(jiàn),類(lèi)中的public靜態(tài)變量作用域是整個(gè)解決方案,可以通過(guò)類(lèi)名使用這個(gè)靜態(tài)變量,而private靜態(tài)變量作用域則是這個(gè)類(lèi),方法中的靜態(tài)變量作用域就是這個(gè)方法
11:.text和.data在文件和虛擬地址都要分配空間 .bss在文件中不分配空間,而要分配虛擬地址空間,因?yàn)樵谖募兴緵](méi)有內(nèi)容
12:鏈接過(guò)程:1空間與地址分配;2符號(hào)解析與重定位 VMA:virtual Memory Address虛擬地址 鏈接前虛擬地址都是空,鏈接后虛擬地址都分配好了
13:目標(biāo)文件代碼段的起始地址以0x00000000開(kāi)始,等到空間分配完成以后,各個(gè)函數(shù)才會(huì)情定自己在虛擬地址空間中的位置
14:在編譯的時(shí)候每個(gè)目標(biāo)文件都會(huì)有一個(gè)符號(hào)表,如果A文件引用了B文件中的變量或方法,那么在符號(hào)表中就會(huì)標(biāo)記這些變量或方法是沒(méi)有定義了,在鏈接的時(shí)候如果沒(méi)有找到這些變量或方法的定義,在鏈接的時(shí)候就會(huì)報(bào)符號(hào)未定義錯(cuò)誤
15:靜態(tài)裝入:程序執(zhí)行是所需要的指令和數(shù)據(jù)必須在內(nèi)存中才能正常運(yùn)行,最簡(jiǎn)單的辦法就是將程序運(yùn)行所需要的指令和數(shù)據(jù)全部裝入內(nèi)存中
動(dòng)態(tài)裝入的基本原理:程序運(yùn)行有局部性,將程序最常用的部分駐留在內(nèi)存中,不太常用的數(shù)據(jù)存放在磁盤(pán)中
16:創(chuàng)建一個(gè)獨(dú)立的虛擬地址空間:將虛擬空間和物理空間映射 讀取可執(zhí)行文件頭,并建立虛擬空間與可執(zhí)行文件的映射關(guān)系:虛擬空間和執(zhí)行文件映射 將CPU的指令寄存器設(shè)置成可執(zhí)行文件的入口地址,啟動(dòng)運(yùn)行
17:Windows平臺(tái)下用C++編寫(xiě)動(dòng)態(tài)鏈接庫(kù)要盡量遵循以下幾個(gè)指導(dǎo)意見(jiàn):
1:所有的接口函數(shù)都應(yīng)該是抽象的,所有的方法都應(yīng)該是純虛的
2:所有的全局函數(shù)都應(yīng)該使用extern C來(lái)防止名字修飾的不兼容,并且導(dǎo)出函數(shù)都應(yīng)是_stdcall調(diào)用規(guī)范的, 這樣即使用戶(hù)本身的程序是默認(rèn)以_cdecl方式編譯的,對(duì)于 DLL的調(diào)用也能夠正確
3:不要使用C++標(biāo)準(zhǔn)庫(kù)STL
4:不要使用異常
5:不要使用虛析構(gòu)函數(shù),可以創(chuàng)建一個(gè)destory方法并且充值delete操作符并調(diào)用destory()方法
6:不要再DLL里面申請(qǐng)內(nèi)存,而且在DLL外釋放(或者相反),不同的DLL和可執(zhí)行文件可能使用不同的堆,在一個(gè)堆里面申請(qǐng)的內(nèi)存而在另一個(gè)堆里面釋放會(huì)導(dǎo)致錯(cuò)誤,對(duì)于內(nèi)存分配相關(guān)的函數(shù)不應(yīng)該是inline的,以防止它在編譯時(shí)被展開(kāi)到不同的DLL和可執(zhí)行文件中
7:不要再接口中使用重載方法,因?yàn)椴煌木幾g器對(duì)于vtable的安排可能不同
18:棧一般保存的內(nèi)容:
1:函數(shù)的返回地址和參數(shù)
2:臨時(shí)變量:包括函數(shù)的非靜態(tài)局部變量以及編譯器自動(dòng)生成的其他臨時(shí)變量
3:保存上下文:包括函數(shù)調(diào)用前后需要保持不變的寄存器
19:多態(tài)的實(shí)現(xiàn)原理:
1:含有虛方法的類(lèi)都有一個(gè)虛函數(shù)表
2:子類(lèi)的虛方法會(huì)覆蓋父類(lèi)對(duì)應(yīng)的虛方法
3:含有虛方法的類(lèi)的每個(gè)實(shí)例都有一個(gè)指向虛方法表的指針,如果虛繼承的話(huà)可能會(huì)有多個(gè)
4:根據(jù)3中的指針調(diào)用虛方法表中對(duì)應(yīng)的虛方法
20:全局構(gòu)造與析構(gòu):
編譯器將兩個(gè)段.init和.finit這兩個(gè)段拼成兩個(gè)函數(shù)_init()和_finit(),這兩個(gè)函數(shù)先后于main函數(shù)執(zhí)行,當(dāng)然main函數(shù)并不是程序的入口,_start才是入口函數(shù),.init段里面有個(gè)數(shù)組,數(shù)組中存放所有全局構(gòu)造函數(shù)的指針,在執(zhí)行函數(shù)_init()時(shí)會(huì)執(zhí)行全局變量的構(gòu)造函數(shù),也就是說(shuō)在調(diào)用main函數(shù)前,全局變量已經(jīng)初始化好了,main函數(shù)執(zhí)行完成之后,在執(zhí)行_finit(),即全局變量的析構(gòu)。
對(duì)每個(gè)編譯單元(.cpp),編譯器會(huì)遍歷其中所有的全局變量,生成一個(gè)特殊的函數(shù)_GLOBAL_I_Hw,這個(gè)函數(shù)的作用就是初始化當(dāng)前編譯單元中的所有全局變量,如果這個(gè)特殊函數(shù)存在(即有全局變量),那么編譯器會(huì)在目標(biāo)文件的.ctors段中存放這個(gè)函數(shù)的一個(gè)指針,連接器在鏈接所有的目標(biāo)文件的時(shí)候,會(huì)將同名的段合并在一起,每個(gè)目標(biāo)文件的.ctors段也就合并在一起了,這樣.ctors段中存放的就是每個(gè)目標(biāo)文件中全局構(gòu)造函數(shù)的指針,執(zhí)行這些全局構(gòu)造函數(shù),全局變量就初始化好了。
全局析構(gòu)的過(guò)程我想聰明的你不看也應(yīng)該知道個(gè)大概,
21:未完.....待續(xù)......
浙公網(wǎng)安備 33010602011771號(hào)