一個(gè)可重入子程序必須滿足下面幾個(gè)性質(zhì):
1、它不能修改代碼指令。在高級(jí)語(yǔ)言中,修改代碼指令是非常難的;但是在匯編語(yǔ)言中,一個(gè)程序要修改自己的代碼并不是一件很難的事。
例如:
mov word [cs:$+7], 5 ; 將5復(fù)制到前面七個(gè)字節(jié)的字中
add ax, 2 ; 前面的語(yǔ)句將2改成了5!
這些代碼在實(shí)模式下可以運(yùn)行,但是在保護(hù)模式下的操作系統(tǒng)上不行,因?yàn)榇a段被標(biāo)識(shí)為只讀。在這些操作系統(tǒng)上,當(dāng)執(zhí)行了上面的第一行代碼,程序?qū)⒈唤K止。這種類型的程序從各個(gè)方面來(lái)看都非常差。它很混亂,很難維護(hù)而且不允許代碼共享(看下面)。
2、它不能修改全局變量(比如在data和bss段里的數(shù)據(jù))。所有的變量應(yīng)儲(chǔ)存在堆棧里。
書寫可重入性代碼有幾個(gè)好處。
1、一個(gè)可重入子程序可以遞歸調(diào)用。
2、 一個(gè)可重入程序可以被多個(gè)進(jìn)程共享。在許多多任務(wù)操作系統(tǒng)上,如果一個(gè)程序有許多實(shí)例正在運(yùn)行,那么只有一份代碼的拷貝在內(nèi)存中。共享庫(kù)和DLL(Dynamic Link Libraries,動(dòng)態(tài)鏈接庫(kù))同樣使用了
這種技術(shù)。
3、可重入子程序可以運(yùn)行在多線程5 程序中。Windows 9x/NT和大多數(shù)類UNIX操作系統(tǒng)(Solaris,Linux,等)都支持多線程程序。
遞歸子程序
這種類型的子程序調(diào)用它們自己。遞歸可以是直接的或是間接的。當(dāng)一個(gè)名為foo的子程序在foo內(nèi)部調(diào)用自己就產(chǎn)生直接遞歸。當(dāng)一個(gè)子程序雖然自己沒(méi)有直接調(diào)用自己,但是其它子程序調(diào)用了它,就產(chǎn)生間接遞歸。
例如:子程序foo可以調(diào)用bar且bar也可以調(diào)用foo。
遞歸子程序必須有一個(gè)終止條件。當(dāng)這個(gè)條件為真時(shí),就不再進(jìn)行遞歸調(diào)用了。如果一個(gè)子程序沒(méi)有終止條件或條件永不為真,那么遞歸將不會(huì)結(jié)束(非常像一個(gè)無(wú)窮循環(huán))。
圖4.15展示了一個(gè)遞歸求n!的函數(shù)。在C中它可以這樣被調(diào)用:
x = fact (3); /* find 3! */
圖4.16展示了上面的函數(shù)調(diào)用的最深點(diǎn)的堆棧狀態(tài)。
圖4.17展示了另一個(gè)更復(fù)雜的遞歸樣例的C語(yǔ)言版而4.18展示了它的匯編語(yǔ)言版。對(duì)于f(3),輸出是什么?注意:每一次遞歸調(diào)用,ENTER指令都會(huì)在堆棧上給新的i值分配空間。因此,f的每一次遞歸調(diào)用都有它自己獨(dú)立的變量i。若是在data段定義i為一雙字,結(jié)果就不一樣了。
回顧一下C變量的儲(chǔ)存類型
C提供了幾種變量?jī)?chǔ)存類型。
global,全局 這些變量定義在任何函數(shù)的外面,且儲(chǔ)存在固定的內(nèi)存空間中(在data或bss段),而且從程序的開(kāi)始一直到程序的結(jié)束都存在。缺省情況下,它們能被程序中的任何一個(gè)函數(shù)訪問(wèn);但是,如果它們被聲明為static,那么只有在同一模塊中的函數(shù)才能訪問(wèn)它們(也就是說(shuō), 依照匯編的術(shù)語(yǔ),這個(gè)變量是內(nèi)部的,不是外部的)。
static,靜態(tài) 在一個(gè)函數(shù)中,它們是被聲明為靜態(tài)的局部變量。(不幸的是,C使用關(guān)鍵字static有兩種目的!)這些變量同樣儲(chǔ)存在固定的內(nèi)存空間中(在data或bss段),但是只能被定義它的函數(shù)直接訪問(wèn)。
automatic,自動(dòng) 它是定義在一個(gè)函數(shù)內(nèi)的C變量的缺省類型。當(dāng)定義它們的函數(shù)被調(diào)用了,這些變量就被分配在堆棧上,而當(dāng)函數(shù)返回了又從堆棧中移除。因此,它們沒(méi)有固定的內(nèi)存空間。
register,寄存器 這個(gè)關(guān)鍵字要求編譯器使用寄存器來(lái)儲(chǔ)存這個(gè)變量的數(shù)據(jù)。這僅僅是一個(gè)要求。編譯器并不一定要遵循。如果變量的地址使用在程序的任意的地方,那么就不會(huì)遵循(因?yàn)榧拇嫫鳑](méi)有地址)。同樣,只有簡(jiǎn)單的整形數(shù)據(jù)可以是寄存器變量。結(jié)構(gòu)類型不可以;因?yàn)樗鼈兊拇笮〔黄ヅ浼拇嫫?!C編譯器通常會(huì)自動(dòng)將普通的自動(dòng)變量轉(zhuǎn)換成寄存器變量,而不需要程序員給予暗示。
volatile,不穩(wěn)定 這個(gè)關(guān)鍵字告訴編譯器這個(gè)變量值隨時(shí)都會(huì)改變。這就意味著當(dāng)變量被更改了,編譯器不能做出任何推斷。通常編譯器會(huì)將一個(gè)變量的值暫時(shí)存在寄存器中,而且在出現(xiàn)這個(gè)變量的代碼部分使
用這個(gè)寄存器。但是,編譯器不能對(duì)不穩(wěn)定類型的變量做這種類型的優(yōu)化。一個(gè)不穩(wěn)定變量的最普遍的例子就是:它可以被多線程程序的兩個(gè)線程修改??紤]下面的代碼:
1 x = 10;
2 y = 20;
3 z = x;
如果x可以被另一個(gè)線程修改。那么其它線程可以會(huì)在第1行和第3行之間修改x的值,以致于z將不會(huì)等于10.但是,如果x沒(méi)有被聲明為不穩(wěn)定類型,編譯器就會(huì)推斷x沒(méi)有改變,然后再將z置為10。
不穩(wěn)定類型的另一個(gè)使用就是避免編譯器為一個(gè)變量使用一個(gè)寄存器。
更多建議: