編譯指令與說明
作者: 錢達智(Wolfgang Chien) wrote on 1996.12.15-19
E-mail: [email protected]
WWW: http://www.aaa.hinet.net/delphi/~chien.htm
{$IFDEF WIN32} -- 這可不是註解喔!
對於Delphi來說﹐左右大括號之間的內容是註解﹐然而「{$」
(左括號後緊接著貨幣符號)對於Compiler(編譯器)而言並不是註解﹐
而是寫給Compiler看的特別指示。
應用時機與場合
Delphi中有許許多多的Compiler Directives(編譯器指令)﹐
這些編譯指令對於我們的程式發展有何影響呢? 它們又能幫我們什麼忙呢?
Compiler Directive 對程式開發的影響與助益, 可以從以下幾個方向來討論:
Ø 協助除錯
Ø 版本分類
Ø 程式的重用與管理
Ø 設定統一的執行環境
協助除錯
穩健熟練的程式設計師經常會在開發應用系統的過程中﹐特別加入
一些除錯程式或者回饋驗算的程序﹐這些除錯程式對於軟體品質的
提升有極其正面的功能。然而開發完成的正式版本中如果不需要這
些額外的程式的話﹐要想在一堆程式中找出哪些是除錯用的程式並加以
刪除或設定為註解﹐不僅累人﹐而且容易出錯﹐況且日後維護時這些除錯程式還用得著。
此時如果能夠應用像是$IFDEF的Compiler Directives ﹐就可以輕易的
指示Delphi要/不要將某一段程式編進執行檔中。
同時﹐Compiler本身也提供了一些錯誤檢查的開關﹐
可以預先對程式中可能的問題提醒程式設計師注意﹐同樣有助於撰寫正確的程式。
版本分類
除了上述的除錯版本/正式版本的分類之外﹐對於像是「試用版」「普及版」
「專業版」的版本分類﹐也可以經由Compiler Directive的使用﹐為最後的
產品設定不同的使用權限。其他諸如「中文版」「日文版」「國際標準版」
等全球版本管理方面﹐同樣也可以視需要指示Delphi特別連結哪些資源檔或
者是採用哪些適當的程式。以上的兩則例子中﹐各版本間只需共用同一份程式碼即可。
Delphi 1.0 與 Delphi 2.0有許多不同之處﹐元件資源檔(.DCR)即是其中
一例﹐兩者的檔案格式並不相容﹐在您讀過本文之後﹐相信可以寫出這樣的程式﹐
指示Delphi在不同的版本採用適當的資源檔以利於元件的安裝。
{$IFDEF WIN32}
{$R XXX32.DCR}
{$ELSE}
{$R XXXX16.DCR}
{$EDNIF}
程式的重用與管理
經過前文的討論後﹐相信你已經不難看出Compiler Directives在程式管理上的應用價值。
對於原始程式的重用與管理﹐也是Compiler Directives 使得上力的地方. 舉例來說:
Pascal-Style字串是Delphi 1.0與 Delphi 2.0之間的明顯差異﹐除了原先的短字串之外﹐
Delphi 2.0之後還多了更為方便使用的長字串﹐同時﹐系統也額外提供了像是 Trim()
這樣的字串處理函式。假如您有一個字串處理單元必須要同時應用於Delphi 1.0 與
2.0的專案時﹐編譯指示器可以幫你的忙。
此外﹐透過像是{$I xxxx} 這樣的 Compiler Directives﹐我們也可以適當的含入某些
程式, 同樣有助於切割組合我們的程式或編譯設定。
設定一致的執行環境
專案小組的成員間﹐必須有共同的環境設定﹐我很難預料一個小組成員間彼此有不同的
{$B}{$H}{$X}設定﹐最後子系統在併入主程式時會發生什麼事。
此外, 當您寫好一個元件或單元需要交予第三者使用時, 使用編譯指示器也可以保證元
件使用者與您有相同的編譯環境。
使用Compiler Directives
指令語法
Compiler Directives從外表看起來與註解頗為類似, 與註解不同的是:
Compiler Directives的語法格式都是以「{$」開始, 不空格緊接一個名
稱(或一個字母)表明給Compiler的特別指示, 再加上其他的開關或參數內容,
最後以右大括號作為指令的結束, 例如:
{$B+}
{$R-}
{$R MyCursor.res}
同時, 就如同Pascal的變數名稱與保留字一樣, Compiler Directives也是
不區分大小寫的。
從指令的語法格式來說Compiler Directives﹐可以進一步分類成以下三種格式:
Ø 開關指令(Switch directives)
這類指令都是單一字母以不空格的方式連接「+」或「-」符號; 或者是開關名
稱以一個空格後連接「ON」或「OFF」來表示作用/關閉某一個編譯指示開關。例如:
{$A+}
{$ALIGN ON}
開關型的編譯指令不一定要分行寫, 它們可以組合在同一個編譯指示的註解符號之間,
但必須以逗號連接, 而且中間不可以有空格, 例如:
{$B+,H+,T-,J+}
游標停留在程式編輯器的任一位置時按下Ctrl+O O, 完整的Compiler Directives
將會全部列於Unit的最上方。
Ø 參數指令(Parameter directives)
有些Compiler Directives需要在編譯名稱後面連接自定的參數(檔案名稱或指定的記
憶體大小), 例如: {$R MyCursor.res}, 即在指示Delphi在編譯連結時,
含入「MyCursor.res」這個資源檔。
Ø 條件指令(Conditional directives)
指示Compiler在編譯的過程中, 按我們設定的條件, 選擇性的採用/排除不同區域的
程式碼。
以下是一個條件編譯的例子, 第一與第三列是寫給Compiler看的,指示
Compiler在 __DEBUG這個條件名稱完成定義的情況才編譯ShowMessage()這列程式;
反之, 如果 __DEBUG 當時沒有定義的話, 這段程式幾乎與註解無異, Compiler對
它將視而不見。
{$IFDEF __DEBUG}
ShowMessage(IntToStr(i));
{$ENDIF}
如何從IDE改變Compiler directives設定
從Delphi的IDE程式整合發展環境, 我們很方便的就可以修改各個compiler directives的
設定, 方法是:
從Delphi IDE主選單: Project/Options/Compiler, 直接核選/取消各個CheckBox。
值得注意的是, 改變一個專案的Compiler directives並不會影響其他的專案, 換言之,
各個專案都保有自己一套編譯指示。
假如您希望其他的專案也採用相同一套的Compiler directives, 在上述Project Options
對話盒的左下方有一個「Default」選項, 選取這個CheckBox之後, 雖然對於既有的專案
沒有作用, 但未來新的專案都將可以採用這組設定作為預設值。
將Compiler directives寫入程式
透過Delphi的整合環境設定Compiler directives的確十分簡便, 但是許多情況下我們
仍然需要將Compiler directive直接加到程式中。至少有兩個原因支持我們這麼作:
Ø 局部控制編譯條件
在Project/Options/Compiler中所作的設定, 影響所及是整個專案, 如果某一段程式
要特別使用不同的編譯設定, 就必須直接將編譯指示加到程式中。
下列這段取自Online Help的程式範例, 即應用了{$I}編譯指令局部控制在發生I/O錯誤
時不要舉發例外訊息, 這樣, 我們就可以編譯出一支在這段程式區域中不會產生I/O例外
訊息的檔案偵測函數。
function FileExists(FileName: string): Boolean;
var
F: file;
begin
{$I-}
AssignFile(F, FileName);
FileMode := 0; ( Set file access to read only }
Reset(F);
CloseFile(F);
{$I+}
FileExists := (IOResult = 0) and (FileName <> '');
end; { FileExists }
Ø 程式的可攜性
我們都可能會用到其他公司或個人創作的unit或component, 也可能分享程式給其他人,
換句話說, 單元或程式可能會在不同的機器上編譯, 直接將Compiler directives加入
程式, 不僅可以免去程式使用前需要特別更改IDE的麻煩, 更重要的是解決了各個單元
間要求不同編譯環境的歧異。
注意事項
Compiler directives的作用與影響範圍
如同變數的可見範圍與生命週期, 在我們使用 Compiler Directives 時也必須注意各個
Compiler Directives 的作用範圍.
Compiler Directives的作用範圍可分為以下兩種:
Ø 全域的
全域的Compiler Directives, 影響所及是整個專案; 我們稍早前提到經由Delphi
IDE改變Compiler directives的方式就屬於全域的設定。
Ø 區域的
而區域的Compiler Directives 影響所及只從Compiler Directives 改變的那一行開始,
直到該程式單元(Unit)的結束或另一個相同的Compiler Directives 為止,
對其他的程式單元並沒有影響。
也就是說, 如果在unit中特別加入Compiler directives, Compiler會優先採用區域
的設定, 然後才是屬於專案層級的全域設定。
值得一提的是, 在程式中直接加入Compiler directives的最大作用範圍也只限於當時
那個單元而已, 對其他單元並沒有任何影響, 即使是以uses參考也是一樣。也就是說,
我們可以透過uses參考其他unit公開的變數與函式, 但是各個unit的編譯指令並不會互
相參考。
這項獨立的性質, 使得unit之間編譯環境的設定與關係變得十分簡潔, 例如Delphi
2.0的VCL都是在{$H+}的情況下編譯的, 因此, VCL中的字串都是以長字串的型態編譯
而成的, 有了這項編譯指令獨立的特性, 不論我們Prject中的設定為何, 這些在VCL中
定義過的字串都是長字串。我們的Project也不會因為uses了VCL中的unit而改變了自己
的設定。
作者: 錢達智(Wolfgang Chien) wrote on 1996.12.15-19
E-mail: [email protected]
WWW: http://www.aaa.hinet.net/delphi/~chien.htm
{$IFDEF WIN32} -- 這可不是註解喔!
對於Delphi來說﹐左右大括號之間的內容是註解﹐然而「{$」
(左括號後緊接著貨幣符號)對於Compiler(編譯器)而言並不是註解﹐
而是寫給Compiler看的特別指示。
應用時機與場合
Delphi中有許許多多的Compiler Directives(編譯器指令)﹐
這些編譯指令對於我們的程式發展有何影響呢? 它們又能幫我們什麼忙呢?
Compiler Directive 對程式開發的影響與助益, 可以從以下幾個方向來討論:
Ø 協助除錯
Ø 版本分類
Ø 程式的重用與管理
Ø 設定統一的執行環境
協助除錯
穩健熟練的程式設計師經常會在開發應用系統的過程中﹐特別加入
一些除錯程式或者回饋驗算的程序﹐這些除錯程式對於軟體品質的
提升有極其正面的功能。然而開發完成的正式版本中如果不需要這
些額外的程式的話﹐要想在一堆程式中找出哪些是除錯用的程式並加以
刪除或設定為註解﹐不僅累人﹐而且容易出錯﹐況且日後維護時這些除錯程式還用得著。
此時如果能夠應用像是$IFDEF的Compiler Directives ﹐就可以輕易的
指示Delphi要/不要將某一段程式編進執行檔中。
同時﹐Compiler本身也提供了一些錯誤檢查的開關﹐
可以預先對程式中可能的問題提醒程式設計師注意﹐同樣有助於撰寫正確的程式。
版本分類
除了上述的除錯版本/正式版本的分類之外﹐對於像是「試用版」「普及版」
「專業版」的版本分類﹐也可以經由Compiler Directive的使用﹐為最後的
產品設定不同的使用權限。其他諸如「中文版」「日文版」「國際標準版」
等全球版本管理方面﹐同樣也可以視需要指示Delphi特別連結哪些資源檔或
者是採用哪些適當的程式。以上的兩則例子中﹐各版本間只需共用同一份程式碼即可。
Delphi 1.0 與 Delphi 2.0有許多不同之處﹐元件資源檔(.DCR)即是其中
一例﹐兩者的檔案格式並不相容﹐在您讀過本文之後﹐相信可以寫出這樣的程式﹐
指示Delphi在不同的版本採用適當的資源檔以利於元件的安裝。
{$IFDEF WIN32}
{$R XXX32.DCR}
{$ELSE}
{$R XXXX16.DCR}
{$EDNIF}
程式的重用與管理
經過前文的討論後﹐相信你已經不難看出Compiler Directives在程式管理上的應用價值。
對於原始程式的重用與管理﹐也是Compiler Directives 使得上力的地方. 舉例來說:
Pascal-Style字串是Delphi 1.0與 Delphi 2.0之間的明顯差異﹐除了原先的短字串之外﹐
Delphi 2.0之後還多了更為方便使用的長字串﹐同時﹐系統也額外提供了像是 Trim()
這樣的字串處理函式。假如您有一個字串處理單元必須要同時應用於Delphi 1.0 與
2.0的專案時﹐編譯指示器可以幫你的忙。
此外﹐透過像是{$I xxxx} 這樣的 Compiler Directives﹐我們也可以適當的含入某些
程式, 同樣有助於切割組合我們的程式或編譯設定。
設定一致的執行環境
專案小組的成員間﹐必須有共同的環境設定﹐我很難預料一個小組成員間彼此有不同的
{$B}{$H}{$X}設定﹐最後子系統在併入主程式時會發生什麼事。
此外, 當您寫好一個元件或單元需要交予第三者使用時, 使用編譯指示器也可以保證元
件使用者與您有相同的編譯環境。
使用Compiler Directives
指令語法
Compiler Directives從外表看起來與註解頗為類似, 與註解不同的是:
Compiler Directives的語法格式都是以「{$」開始, 不空格緊接一個名
稱(或一個字母)表明給Compiler的特別指示, 再加上其他的開關或參數內容,
最後以右大括號作為指令的結束, 例如:
{$B+}
{$R-}
{$R MyCursor.res}
同時, 就如同Pascal的變數名稱與保留字一樣, Compiler Directives也是
不區分大小寫的。
從指令的語法格式來說Compiler Directives﹐可以進一步分類成以下三種格式:
Ø 開關指令(Switch directives)
這類指令都是單一字母以不空格的方式連接「+」或「-」符號; 或者是開關名
稱以一個空格後連接「ON」或「OFF」來表示作用/關閉某一個編譯指示開關。例如:
{$A+}
{$ALIGN ON}
開關型的編譯指令不一定要分行寫, 它們可以組合在同一個編譯指示的註解符號之間,
但必須以逗號連接, 而且中間不可以有空格, 例如:
{$B+,H+,T-,J+}
游標停留在程式編輯器的任一位置時按下Ctrl+O O, 完整的Compiler Directives
將會全部列於Unit的最上方。
Ø 參數指令(Parameter directives)
有些Compiler Directives需要在編譯名稱後面連接自定的參數(檔案名稱或指定的記
憶體大小), 例如: {$R MyCursor.res}, 即在指示Delphi在編譯連結時,
含入「MyCursor.res」這個資源檔。
Ø 條件指令(Conditional directives)
指示Compiler在編譯的過程中, 按我們設定的條件, 選擇性的採用/排除不同區域的
程式碼。
以下是一個條件編譯的例子, 第一與第三列是寫給Compiler看的,指示
Compiler在 __DEBUG這個條件名稱完成定義的情況才編譯ShowMessage()這列程式;
反之, 如果 __DEBUG 當時沒有定義的話, 這段程式幾乎與註解無異, Compiler對
它將視而不見。
{$IFDEF __DEBUG}
ShowMessage(IntToStr(i));
{$ENDIF}
如何從IDE改變Compiler directives設定
從Delphi的IDE程式整合發展環境, 我們很方便的就可以修改各個compiler directives的
設定, 方法是:
從Delphi IDE主選單: Project/Options/Compiler, 直接核選/取消各個CheckBox。
值得注意的是, 改變一個專案的Compiler directives並不會影響其他的專案, 換言之,
各個專案都保有自己一套編譯指示。
假如您希望其他的專案也採用相同一套的Compiler directives, 在上述Project Options
對話盒的左下方有一個「Default」選項, 選取這個CheckBox之後, 雖然對於既有的專案
沒有作用, 但未來新的專案都將可以採用這組設定作為預設值。
將Compiler directives寫入程式
透過Delphi的整合環境設定Compiler directives的確十分簡便, 但是許多情況下我們
仍然需要將Compiler directive直接加到程式中。至少有兩個原因支持我們這麼作:
Ø 局部控制編譯條件
在Project/Options/Compiler中所作的設定, 影響所及是整個專案, 如果某一段程式
要特別使用不同的編譯設定, 就必須直接將編譯指示加到程式中。
下列這段取自Online Help的程式範例, 即應用了{$I}編譯指令局部控制在發生I/O錯誤
時不要舉發例外訊息, 這樣, 我們就可以編譯出一支在這段程式區域中不會產生I/O例外
訊息的檔案偵測函數。
function FileExists(FileName: string): Boolean;
var
F: file;
begin
{$I-}
AssignFile(F, FileName);
FileMode := 0; ( Set file access to read only }
Reset(F);
CloseFile(F);
{$I+}
FileExists := (IOResult = 0) and (FileName <> '');
end; { FileExists }
Ø 程式的可攜性
我們都可能會用到其他公司或個人創作的unit或component, 也可能分享程式給其他人,
換句話說, 單元或程式可能會在不同的機器上編譯, 直接將Compiler directives加入
程式, 不僅可以免去程式使用前需要特別更改IDE的麻煩, 更重要的是解決了各個單元
間要求不同編譯環境的歧異。
注意事項
Compiler directives的作用與影響範圍
如同變數的可見範圍與生命週期, 在我們使用 Compiler Directives 時也必須注意各個
Compiler Directives 的作用範圍.
Compiler Directives的作用範圍可分為以下兩種:
Ø 全域的
全域的Compiler Directives, 影響所及是整個專案; 我們稍早前提到經由Delphi
IDE改變Compiler directives的方式就屬於全域的設定。
Ø 區域的
而區域的Compiler Directives 影響所及只從Compiler Directives 改變的那一行開始,
直到該程式單元(Unit)的結束或另一個相同的Compiler Directives 為止,
對其他的程式單元並沒有影響。
也就是說, 如果在unit中特別加入Compiler directives, Compiler會優先採用區域
的設定, 然後才是屬於專案層級的全域設定。
值得一提的是, 在程式中直接加入Compiler directives的最大作用範圍也只限於當時
那個單元而已, 對其他單元並沒有任何影響, 即使是以uses參考也是一樣。也就是說,
我們可以透過uses參考其他unit公開的變數與函式, 但是各個unit的編譯指令並不會互
相參考。
這項獨立的性質, 使得unit之間編譯環境的設定與關係變得十分簡潔, 例如Delphi
2.0的VCL都是在{$H+}的情況下編譯的, 因此, VCL中的字串都是以長字串的型態編譯
而成的, 有了這項編譯指令獨立的特性, 不論我們Prject中的設定為何, 這些在VCL中
定義過的字串都是長字串。我們的Project也不會因為uses了VCL中的unit而改變了自己
的設定。
解决方案 »
- 问一个很弱的问题,如何对Label.Caption的某几个字符变换字体或颜色吗?
- 两数相除取整,一个非常奇怪的错误,请高手们进来看看,在线等
- dbgrid控件的一个使用问题?
- 为何会有“List index out of bounds(0)”错误?
- 入门者问题:如何达到:?在edit框内输入 回车后 在label内显示
- D6里的QReport,如何实现QRDBimage的显示?
- 请教高手?????(急)
- 一个关于字符转换成货币的简单问题。请各位师兄们都帮忙~!谢谢~!!
- ◆◆熟悉ZipTv的请进,这段程序执行后,怎么显示解压的文件是0??◆◆
- 请问Delphi能不能将mdi的子窗体做成DLL,(请具体一点!谢谢!)
- 如何保证delphi程序与sql_server的可靠连接?
- QuickReport在WIN2000下如何自定义打印(高分求教)
directives, 別擔心, 即使別的unit以uses參考了我們的程式, 也不影響它自己原來
的設定。
如果我們自行以{$DEFINE _DEBUGVERSION}($DEFINE在稍後的個別指令介紹中將有說明)
定義了一個條件符號, 這個新的條件符號也是區域的, 換句話說, 它只從定義的那一
個單元的那一列之後才成立, 當然, 也只對目前這個單元有效.
由於自訂的條件符號只有區域的作用, 如果有好幾個程式單元都需要參考到某一個條
件符號, 怎麼辦呢? 嗯! 在每一個程式單元開頭處中都加上編譯指示是最直接的方式,
可是略嫌麻煩, 特別是編譯指示有變時, 要一一修正各個單元的設定內容, 很容易因為
疏忽而出錯。
比較簡易可行的作法是從Delphi IDE整合發展環境的主選單-Project / Options /
Directories/Conditional 的 Conditionals 中填入條件名稱。這樣, 相對於專案的
各個unit而言, 就有了一個全域的條件符號。
或者, 您也可以參考本文對於{$I}這個Compiler Directive的說明。 我在那裏指出了
另一個彈性的解決方式。
修改過編譯指令後, 建議Build All過一次程式
請試一試這個程式:
procedure TForm1.Button1Click(Sender: TObject);
begin
// ifopt是用來偵測某一個編譯開關的作用狀態
{$ifopt H+}
ShowMessage('H+');
{$else}
ShowMessage('H-');
{$endif}
end;
在我們執行上述程式時, 在Delphi預設的是$H+時, ShowMessage()會在畫面上會顯示
「H+」, 執行過後, 讓程式與form的內容與位置保留不變, 單純的從主選單:
Project/Options/Compiler, 將Huge Strings的核對方塊清除($H-),
然後按下F9執行, 咦! 怎麼還是看到「H+」?!
那是因為Delphi只會在unit內容經過異動後才會重新將.PAS編譯成.DCU,
在我們的例子中, 程式並沒有變動, .DCU當然也沒有重新產生, 最後.EXE的這個部分
自然也是沒什麼變化。
所以, 要解決這個問題, 只要以Delphi IDE主選單Project/Build All指示Delphi
重新編譯全部的程式即可。因此, 如果您從Delphi IDE修改過Compiler Directives後,
記得要Build All喔!
不應該用來作為程式執行流程控制
在程式中, 我們可以使用if敘述, 根據執行當時的情況控制程式執行時的流程,
但我們不可以用{$IFDEF}來作同樣的事, 為什麼? 從上述的說明, 相信您不難發現,
Compiler directives會對最後.EXE的內容發生直接的影響, 應用像是{$IFDEF}指示
Compiler的結果, 幾乎可以視同授權Compiler在編譯的那個時候自動選用/捨棄程式
到.DCU, .EXE中, 換句話說, 在程式編譯完成時, 會執行到那一段程式已成定局了,
我們自然不能用它來作程式流程的控制。
條件編譯的巢套最多可以16層
在使用{$IFDEF}…{$ENDIF}條件編譯我們的程式時, 一個{$IFDEF}中可以再包含另一
個{$IFDEF}, 但深度最多只能16層, 雖然是個限制, 但以正常的情形來說, 這應該已
經足夠了。
有些Compiler directives不應寫在Unit中
對於像是{$MINSTACKSIZE}{$MAXSTACKSIZE}管理堆疊大小, 或者像是{$APPTYE}
指示程式編譯成圖形/文字模式的Compiler directives, 只能寫在.DPR中, 寫在Unit
中是沒有效果的。
建議事項
確定您瞭解指令的影響
由於編譯指令的影響是如此直接與深遠, 在修改與應用某一個Compiler directive時,
請確定您已經了解其含意與影響。
打開全部的偵錯開關
Delphi有關偵錯的Compiler directives如下:
Ø $HINTS ON
Ø $D+
Ø $L+
Ø $Q+
Ø $R+
Ø $WARNINGS ON
各指令的用法您可以參閱本章稍後對個別指令的說明, 全部打開這些開關吧!
這樣不僅讓您可以使用Delphi IDE的除錯器, 對於程式編譯與執行過程中的問題,
Delphi也會適時的反應, 有助於寫作正確的程式。
此處有一個迷思有待澄清—「加入Dubug資訊會不會讓執行檔變大變慢啊?」, 不一定。
對於們像是$D+, $L+, $HINTS ON這些開關, 打開後, Delphi在編譯時的確會額外加入
一些除錯資訊, 使得.DCU的檔案變大, 對於.EXE的檔案大小並沒有影響; 同時,
程式的執行速度也沒有改變, 還可以應用IDE的除錯器trace我們的程式, 值得應用。
對於像是$Q, $R等Compiler directive, 的確會影響執行檔的大小與速度, 然而這並
不動搖我們在研發期間使用它們的決定, 請想想看, 值得為這一點點的速度放棄程式
的正確性嗎? 當然, 程式開發完成後, 正式出貨的版本, 可以關閉這兩個開關。
如果您寫好了一個元件, 而且只預備提供.DCU, 由於沒有.PAS可供Delphi IDE的Debugger
追蹤程式, 除錯開關反而應該在元件脫手前關閉並重新編譯.DCU, 否則會引起使用者那
邊找不到檔案的例外訊息。
善用{$I}
{$I FileName}是一個非常有用的Compiler directive.應用這個指令, 我們可以彈性的
管理Compiler directive的設定。
條件名稱請加入前置字元
不知道您有沒有這個疑問 -- 如果用{$DEFINE}定義的條件名稱與變數名稱相同時會發
生什麼事?
procedure TForm1.Button1Click(Sender: TObject);
var
TEST: integer;
begin
{$DEFINE TEST}
{$IFDEF TEST}
ShowMessage('Test');
{$ENDIF}
end;
以上的程式編譯與執行都沒有問題, 但條件名稱與變數名稱重覆畢意容易讓人混淆, 因此,
假如能適當的為編譯條件名稱之前加上諸如底線(_TEST), 程式會比較容易閱讀。
設定一致的編譯環境
在您了解了Compiler Directives之後, 請立即開始著手修改您IDE中有關編譯指示
的各個開關並且設為Default, 這樣, 日後您的專案乃至整個研發小組都將擁有
共同一致的編譯環境, 對於寫出來的程式會以何種方式編譯連結都了然於胸,
直接有助於子系統順利併入主系統中。
個別指令說明
有了之前對於Compiler directives的觀念之後, 接下來的這一節我將一一
介紹幾個常用的Compiler Directive的用法與注意事項, 您可以從這一
節中學到更多有關Compiler directives的知識與使用細節。
{$A+} 欄位對齊
在{$A+}(預設值)的情形下, 如果沒有使用 packed 修飾詞宣告的 record
型態, 其欄位會以CPU可以有效存取的方式向 1. 2. 4 等邊界對齊,
以獲取最佳的存取速度。以下列的程式示例來說:
{$A+}
type
MyRecord = record
ByteField: byte;
IntegerField: integer;
end;
…
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(SizeOf(MyRecord)));
end;
ShowMessage在{$A+}時顯示的結果是:「8」; 倘若是{$A-}, 那所得的結果是「5」,
按理說, Byte應該只要一個byte就足夠了, 但是考慮到硬體的執行特性,
經過對齊後的record會有比較好的執行速度。
有關這個Compiler Directive要注意的事項是: 不管{$A}的開關是ON或OFF,
使用packed修飾過的記錄宣告, 是一定不會對齊的. 例如:
MyRecord = packed record // 不會對齊的記錄宣告方式
{$APPTYPE GDI} 應用程式型態
一般的情形下, Delphi會以{$APPTYPE GUI}的方式產生一個圖形的使用者介面程式,
如果您需要產生一個文字螢幕模式的程式, 那可以經由:
Ø 在.DPR中加入{$APPTYPE CONSOLE}
Ø 從主選單: Project/Options/Linker/EXE and DLL Options, 核取
「Generate Console Application」Check Box。
其他有關這個Compiler Directive的注意事項有:
Ø $APPTYPE不能應用在DLL的專案或單一的程式單元(Unit), 它只對.EXE有意義。
而且只有寫在.DPR中才有作用。
Ø 我們可以應用System程式單元中的IsConsole函數在程式執行時偵測應用程式
的類型。
Ø 參閱Object Pascal手冊第十三章可以知道更多有關Console Mode
Application的資訊。
請看以下的程式:
if (Length(sCheckedDateString) <> 8)
or EmptyStr(sCheckedDateString)
or (sCheckedDateString = ' . . ')
or (sCheckedDateString = ' / / ') then
begin
Result := True;
Exit;
end;
假如sCheckedDateString的字串內容是「85/12/241」(長度9)的話, 以上的if述句,
其實在第一個邏輯判斷時就已經知道結果了, 即使不看後來的邏輯運算結果也知道
整個式子會是真值。
假如您希望對整個邏輯運算式進行完整的評估 -- 儘管結果已知, 後來的邏輯運算
也不影響整個的結果時仍要全部評估過, 請將這個Compiler directives設為{$B+},
反之, 請設為{$B-}, 系統的預設值是{$B-}。
{$D+} 除錯資訊
當程式以{$D+}(預設值)編譯時, 我們可以用Delphi整合發展境境的Debugger設定
中斷點, 也可以使用Trace Into或Trace Info追蹤程式的執行過程, 值得注意的是,
以{$D+}編譯的程式, 執行的速度並不會受到影響, 只不過編譯過的DCU的檔案長度會
加大, 但EXE檔的大小不變。
{$DEFINE條件名稱} 定義條件名稱
隨著您對Compiler Directives的瞭解與應用程度的加深, 您會發現這是一個非常實
用的編譯指示。
經常, 我們會因為除錯需要﹑區別不同版本等緣故, 希望選擇性的採用或排除某一
段程式, 這個時候, 我們就可以先以$DEFINE定義好一個條件名稱(Conditional name),
然後配合{$IFDEF條件名稱}…{$ELSE}…{$ENDIF}指示編譯器按指定的條件名稱之有無
來選擇需要編譯的程式。以下列的程式片斷來說:
{$DEFINE _ProVersion}
…
procedure TForm1.Button1Click(Sender: TObject);
begin
{$IFDEF _Proversion}
frmPrint.ShowModal; // A
{$ELSE}
ShowMessage('很抱歉, 試用版不提供列印功能');
{$ENDIF}
end;
編譯器將會選擇編譯上述A的那列程式, 日後, 如果我們需要編譯「簡易版」
的程式版本時, 只要:
Ø 將{$DEFINE _ProVersion}那列整個刪掉。
Ø 或者, 將{$DEFINE _ProVersion}改成{-$DEFINE _ProVersion},
讓它變成普通的註解
Ø 或者, 在{$DEFINE _ProVersion}的下一列加上{$UNDEF _ProVersion},
解除_ProVersion這個條件名稱的定義。
這樣, 由於_ProVersion這個條件名稱未定義的緣故, Compiler就只會選擇
{$ELSE}下的那段程式, 重新編譯一次, 不需費太多力氣, 很容易的就可以製作出
「簡易版」了, 省去了要同時維護兩份程式的麻煩。
使用$DEFINE時的其他注意事項如下:
Ø 以{$DEFINE}定義的條件名稱都是區域的。換句話說, 它的作用範圍只在
當時所在的單元才有效, 即使定義在unit的interface, 由其他的unit以uses參考也沒有效,
仍然只有在目前的unit有作用。
Ø 此外, 它的作用範圍是從定義起, 到unit結尾或者以{$UNDEF}解除為止。
Ø 如果程式單元中已經用{$DEFINE}定義了一個條件名稱, 而且也沒有用
{$UNDEF}解除定義, 重新{$DEFINE}一個同樣名稱並沒有作用, 換句話說, 它們是同一個.
Ø 假如需要一個全域的條件名稱, 您可以:主選單: Project / Options / Directories/
Conditional 的 Conditionals 中填入條件名稱。
Ø 以下的標準條件名稱, 是Delphi 2.0已經預先預備好的, 我們可以直接引用,
同時, 它們都是全域的, 任何Unit都可以參照得到。
Ø VER90: Delphi Object Pascal的版本編號。90表示9.0版, 日後若出現9.5
版時, 也會有VER95的定義。
Ø WIN32: 指出目前是在Win32(95, NT)作業環境
Ø CUP386: 採用386(含)以上的CPU時, 系統會提供本條件名稱。
Ø CONSOLE: 此符號會於應用程式是在螢幕模式下編譯時才定義。