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