C++ Tricks
By FarseerFc
從今天起,我再將在Live Space和QQZone同時發表一系列文章,暫定名爲“C++Tricks”。
本文旨在記錄和闡述一些本人學習C++時所得的心得、技巧。總體來看,本文涉及的內容是每一個C++程序員都應該知道的,但是很少見諸C++教材。希望對各位同仁學習C++有所幫助。
C++ Tricks
By FarseerFc
從今天起,我再將在Live Space和QQZone同時發表一系列文章,暫定名爲“C++Tricks”。
本文旨在記錄和闡述一些本人學習C++時所得的心得、技巧。總體來看,本文涉及的內容是每一個C++程序員都應該知道的,但是很少見諸C++教材。希望對各位同仁學習C++有所幫助。
函數使用棧來保存局部變量,傳遞函數參數。進入函數時,函數在棧上爲函數中的變量統一預留棧空間,將esp減去相應字節數。當函數執行流程途徑變量聲明語句時,如有需要就調用相應構造函數將變量初始化。當執行流程即將離開聲明所在代碼塊時,以初始化的順序的相反順序逐一調用析構函數。當執行流程離開函數體時,將esp加上相應字節數,歸還棧空間。
爲了訪問函數變量,必須有方法定位每一個變量。變量相對於棧頂esp的位置在進入函數體時就已確定,但是由於esp會在函數執行期變動,所以將esp的值保存在ebp中,並事先將ebp的值壓棧。隨後,在函數體中通過ebp減去偏移量來訪問變量。以一個最簡單的函數爲例:
void f()
{
int a=0; //a的地址被分配爲ebp-4
char c=1; //c的地址被分配爲ebp-8
}
產生的彙編代碼爲:
push ebp ;將ebp壓棧
mov ebp,esp ;ebp=esp 用棧底備份棧頂指針
sub …
當調用一個函數時,主調函數將參數以聲明中相反的順序壓棧,然後將當前的代碼執行指針(eip)壓棧,然後跳轉到被調函數的入口點。在被調函數中,通過將ebp加上一個偏移量來訪問函數參數,以聲明中的順序(即壓棧的相反順序)來確定參數偏移量。被調函數返回時,彈出主調函數壓在棧中的代碼執行指針,跳回主調函數。再由主調函數恢復到調用前的棧。
函數的返回值不同於函數參數,通過寄存器傳遞。如果返回值類型可以放入32位變量,比如int、short、char、指針等類型,通過eax寄存器傳遞。如果返回值類型是64位變量,如_int64,同過edx+eax傳遞,edx存儲高32位,eax存儲低32位。如果返回值是浮點類型,如float和double,通過專用的浮點數寄存器棧的棧頂返回。如果返回值類型是用戶自定義結構,或C++類類型,通過修改函數簽名,以引用型參數的形式傳回。
同樣以最簡單的函數爲例:
void f(){
int i …
首先提問,既然I386上sizeof(int)==4、sizeof(char)==1,那麼如下結構(struct)A的sizeof是多少?
struct A{int i;char c;};
答案是sizeof(A)==8……1+5=8?
呵呵,這就是I386上的邊界對齊問題。我們知道,I386上有整整4GB的地址空間,不過並不是每一個字節上都可以放置任何東西的。由於內存總線帶寬等等的技術原因,很多體系結構都要求內存中的變量被放置於某一個邊界的地址上。如果違反這個要求,重則導致停機出錯,輕則減慢運行速度。對於I386平臺而言,類型爲T的變量必須放置在sizeof(T)的整數倍的地址上,char可以隨便放置,short必須放在2的整數倍的地址上,int必須放在4的整數倍的地址上,double必須放在8的整數倍的地址上。如果違反邊界對齊要求 …
基於前文(2.4節)分析,我們可以不通過函數簽名,直接通過指針運算,來得到函數的參數。由於參數的壓棧和彈出操作都由主調函數進行,所以被調函數對於參數的真實數量不需要知曉。因此,函數簽名中的變量聲明不是必需的。爲了支持這種參數使用形式,C語言提供可變參數表。可變參數表的語法形式是在參數表末尾添加三個句點形成的省略號“...”:
void g(int a,char* c,...);
省略號之前的逗號是可選的,並不影響詞法語法分析。上面的函數g可以接受2個或2個以上的參數,前兩個參數的類型固定,其後的參數類型未知,參數的個數也未知。爲了知道參數個數,我們必須通過其他方法,比如通過第一個參數傳遞:
g(3,”Hello”,2,4,5);//調用g並傳遞5個參數,其中後3個爲可變參數。
在函數的實現代碼中,可以通過2.4節敘述的 …
參數壓棧順序:逆序(從右至左)
參數堆棧恢復者:主調函數(caller)
參數壓棧順序:逆序(從右至左)
參數堆棧恢復者:被調函數(callee)
所謂X86體系結構,是指以Intel 8086芯片爲首的芯片所沿襲的CPU結構,一些文檔中又被稱作IA32體系結構。包括的芯片有但不限於:Intel 8086至 80486,奔騰(Pentium)系列處理器1至4,賽揚系列處理器,酷睿系列處理器,以及AMD的相應型號產品。X86體系結構在早期屬於16位處理器,自80386之後擴展爲32位處理器,所以一些文檔中又把80386之後的32位處理器體系稱作I386。自Pentium4後期,AMD的Athlon64開始,I386被進一步擴充爲64位處理器,含有64位尋址能力的X86體系結構被稱作X86-64或IA32-64。總之,市售的個人電腦用CPU,除蘋果的Macintosh之外,全部採用X86體系結構芯片。
在X86早期,16位的尋址能力只支持64KB(2^16=64K)內存,這顯然是不夠的。Intel採用分段尋址的方法,用4位段位+16位偏移量,提供了總共1MB(2^20=1M)的尋址能力。所以在X86的16位編程中,有兩種指針類型 …
很多人甚至不知道逗號(,)也是個C++運算符。與語法上要求出現的逗號(比如分隔函數參數的逗號)不同的是,出現在表達式中的逗號運算符在語義上表示多個表達式操作的連續執行,類似於分隔多語句的分號。比如:
for(inti=0,j=9;i<10;++i,--j)std::cout<<i<<”+”<<j<<”=9\n”;
在這句語句中,出現了兩個逗號,其中前者是語法上用來分隔聲明的變量的,並非逗號運算符,而後者則是一個逗號運算符。根據C++標準,逗號運算符的執行順序爲從左到右依次執行,返回最後一個子表達式的結果。由於只有最後一個表達式返回結果,所以對於一個語義正常的逗號表達式而言,前幾個子表達式必須具有副作用。同時,從語言的定義中也可以看出,逗號表達式對求值的順序有嚴格要求 …
條件運算符(?:)是C++中唯一的三目運算符(trinary operator),用於在表達式中作條件判斷,通常可以替換if語句,與Visual Basic中的iif函數、Excel中的if函數有同樣的作用。語法形式如下:
condition ? true_value : false_value
其中condition *條件是任何可以轉換爲bool類型的表達式,包括但不僅限於**bool*、int、指針。與if和while的條件部分稍顯不同的是,這裏不能定義變量,否則會導致語法錯誤。
另外,條件語句會切實地控制執行流程,而不僅僅是控制返回值。也就是說,兩個返回值表達式中永遠只有一個會被求值,在表達式的執行順序很重要時,這點尤爲值得注意。比如:
int *pi=getInt();
int i=pi …
填補信仰、喚醒良知
我們聽盡了呼籲與號召,對於良知,我不必譴責喪失它的國人,不必盛讚良知的美好。我只想討論,喪失了良知的原因——空缺的信仰。
一、空缺信仰喪失良知
現代的國人缺少信仰,以至於喪失良知。曾幾何時,中華民族由良好的信仰凝聚而成。三皇五帝時,族民們以炎黃爲信仰;春秋戰國時,士大夫之族以周制禮樂爲信仰;漢代以後,百姓延習孔孟之說、老聃之道,以儒家學說爲信仰;自大唐起,以佛教爲首的現代宗教紛紛傳入中原,人民開始以它們作爲信仰。
直至鴉片戰爭、五四運動,西方文化入侵中華,國人開始拋棄國學,轉而去研究科學;文化大革命,十年文化浩劫,人們批判舊的信仰,卻沒有合適的新的信仰前來填補。從此,國人的信仰出現空缺,國人的良知也被一塊塊蠶食殆盡。
二、信仰、科學、迷信
在許多國人的心目中,信仰就等於迷信。從小到大的教育告訴我們 …