第四纪冰川 發表於 2020-4-30 10:29:00

Delphi编译开关(转帖收藏)

<p>Delphi编译指令详解(1-10)<br>发表:不详&nbsp;&nbsp; 阅读:次&nbsp; 关键字:不详&nbsp;&nbsp; 字体:[大 中 小]<br>Delphi编译指令详解(1)——总述</p>
<p>Delphi编译指令共有三种,开关编译指令、参数编译指令和条件编译指令。</p>
<p>一个编译指令实际上是一个标志符(编译器所能识别的标志符),且有其固定的语法。<br>编译器根据不同的指令按不同的方式编译程序。<br>你可以将一个编译指令放在程序的任何位置。编译指令的开始字符是“ $“ ,<br>它总是出现在 “{“符号之后,$之后为编译指令的实际部分,可以是一个或<br>多个字符(根据指令的不同而定),在指令后还可以包含一个或多个需要的参数。<br>一个编译指令的结束符为“}”。</p>
<p>下面分别讲述三种编译指令</p>
<p>开关编译指令地特点是它有 on 、off 两种转换状态。对于单字符版本,<br>你可以在编译指令后添加 "+"或“-”。对于长字符版本,你可以在编译指令后添加<br>“on”或“off”。</p>
<p><br>开关编译指令又分为全局和局部两种。</p>
<p>全局编译指令影响全部的编译内容,它必须在放在程序和单元声明之前。</p>
<p>局部编译指令仅影响局部的编译内容,它的影响范围是,直至下一个最近的编译指令。<br>它可以放在程序的任意位置。</p>
<p>开关编译指令可以组合成一个简单的编译指令组,中间用逗号分开,但不能有空格。<br>例如:<br>{$B+,R-,S-}</p>
<p><br>参数编译指令通过参数指定影响的的内容,参数可以为文件名或内存大小等。</p>
<p><br>条件编译指令指定的条件如何编译特定的区域段。也就是如果满足某个条件,<br>则按照一种方式编译,否则按另一种方式编译。</p>
<p>所有的编译指令(除开关编译指令外)在名字和参数之间至少应有一个空格。<br>例如:<br>{$B+}</p>
<p>{$STACKCHECKS ON}<br>{$R- Turn off range checking}<br>{$I TYPES.INC}<br>{$M 32768,4096}<br>{$DEFINE Debug}<br>{$IFDEF Debug}</p>
<p>{$ENDIF}</p>
<p><br>Delphi编译指令详解(2)——排列字段类型(Align fields)</p>
<p>Type Switch //开关编译指令<br>Syntax {$A+}, {$A-}, {$A1}, {$A2}, {$A4}, or {$A8}<br>{$ALIGN ON}, {$ALIGN OFF}, {$ALIGN 1}, {$ALIGN 2}, {$ALIGN 4}, or {$ALIGN 8}<br>Default {$A8}<br>{$ALIGN 8}<br>Scope Local //局部指令</p>
<p>说明:</p>
<p>指令 $A 控制Delphi中的记录类型字段和类结构型字段。</p>
<p>在指令 {$A1} 或 {$A-}状态下,字段是无序的,所有的记录和类结构都被积压在一起,无序排放。</p>
<p>在指令 {$A2} 下,记录型的字段在声明时没有packed,类型结构中的字段,按字节顺序排列。</p>
<p>在指令 {$A4} 下,记录型的字段在声明时没有packed,类型结构中的字段,按双字节顺序排列。</p>
<p>在指令 {{$A8} 或 {$A+} 下,记录型的字段在声明时没有packed,类型结构中的字段,按四字节顺序排列。</p>
<p>在指令 $A 下,无论变量类型,常量类型,总是以最佳存取方式排列。在 {$A8} 下,按最快的方式排列。</p>
<p><br>Delphi编译指令详解(3)——注释与编译指令的区别与联系(Comments and compiler directives)</p>
<p><br>注释的内容将被编译器忽略,除非它们是离析器函数或编译指令。</p>
<p>这里有几种常见的注释结构</p>
<p>{ 括在此大括号中的内容为注释内容 ,此内容可以为多行,只要在两个大括号之间即可以,注释掉的内容一般都会以特殊的字体和颜色显示(适合所有的注释类型),很容易区分}<br>(* 在此括号和星号之间的内容为注释内容 *)<br>// 在此双反斜线后的内容为注释内容,此处只能为一行。.</p>
<p><br>注释不能被嵌套。如{{}}将不起作用,但是(*{}*)是允许的。</p>
<p>还有一种注释它跟普通的注释很像,但它有特殊的一点 以 "{$" 开始,以“}“,此种注释称为编译指令。<br>如<br>{$WARNINGS OFF}</p>
<p>它将告诉编译器不要产生错误警告信息。</p>
<p>说白了 编译指令 不是一种注释,而是一种告诉编译器如何编译的指示符。</p>
<p><br>Delphi编译指令详解(4)——应用程序类型</p>
<p>Type Parameter //参数指令<br>Syntax {$APPTYPE GUI} or {$APPTYPE CONSOLE}<br>Default {$APPTYPE GUI}<br>Scope Global //局部指令</p>
<p>说明</p>
<p><br>$APPTYPE 编译指令决定是否产生 Win32 控制台程序(以DOS方式显示)或<br>图形界面程序(以窗口显示,大部分为此程序)。</p>
<p>在{$APPTYPE GUI}编译指令下,编译器产生 图形界面程序,这是一种普通的Delphi应用程序。</p>
<p>在{$APPTYPE CONSOLE}编译指令下,编译器产生一个控制台应用程序。<br>当一个控制台程序启动的时候,出现一个控制台窗口,通过此窗口用户可以和应用程序交互。<br>此时标准的输入输出命令与控制台程序自动结合。</p>
<p>IsConsole布尔变量在System单元中,通过它可以查看程序是否运行在控制台程序下或图形界面程序下。</p>
<p>$APPTYPE编译指令只能出现在程序中。他不可以出现在 库文件 单元文件 和包中。</p>
<p>&nbsp;</p>
<p>Delphi编译指令详解(5)——声明称指令(Assert directives)</p>
<p>Type Switch //开关类型<br>Syntax {$C+} or {$C-}<br>{$ASSERTIONS ON} or {$ASSERTIONS OFF}<br>Default {$C+}<br>{$ASSERTIONS ON}<br>Scope Local //局部</p>
<p>说明</p>
<p>$C 指令决定在Delphi资源文件中能否产生声明代码。{$C+}为默认指令。</p>
<p>此声明通常不用在运行时中的测试版本产品中。</p>
<p><br>Delphi编译指令详解(6)——调试信息</p>
<p>Type Switch //开关类型<br>Syntax {$D+} or {$D-}<br>{$DEBUGINFO ON} or {$DEBUGINFO OFF}<br>Default {$D+}<br>{$DEBUGINFO ON}<br>Scope Global //局部指令</p>
<p>说明:</p>
<p><br>$D 指令决定能否产生调试信息。这个信息是由每个进程产生的一个成员列表,<br>并记录对象代码地址在资源文件中的行号。</p>
<p>对于单元文件,调试信息记录在和单元文件一起的单元对象代码中。<br>调试信息增加单元文件的大小,当编译程序编译单元文件是将增加而外的内存。<br>但它不影响可执行程序的大小和速度。</p>
<p>当一个程序或单元文件在{$D+}状态下被编译,综合调试允许你在此模块中单步运行和设置断点。</p>
<p>$D指令通常和 $L一起使用,他们能决定是否产生局部调试的符号信息。</p>
<p><br>Delphi编译指令详解(7)——库文件与共享对象</p>
<p>Type Parameter //参数编译指令<br>Syntax $LIBPREFIX 'string'<br>$LIBSUFFIX 'string'<br>$LIBVERSION 'string'<br>Defaults $LIBPREFIX 'lib' or $SOPREFIX 'bpl'<br>$LIBSUFFIX ' '<br>$LIBVERSION ' '<br>Scope Global //局部指令</p>
<p>说明:</p>
<p><br>$LIBPREFIX 指令可以覆盖 默认的 'lib' 或 'bpl'输出文件前缀。例如你可以指定<br>{$LIBPREFIX 'dcl'}</p>
<p>对于一个设计时间包,你可以使用下面的指令去所有的前缀。<br>{$LIBPREFIX ' '}</p>
<p>$LIBSUFFIX 指令添加一个指定的后缀(在扩展明之前)到输出文件名中。<br>例如,使用<br>{$LIBSUFFIX '-2.1.3'}<br>添加到something.pas中,将产生<br>something-2.1.3.dll 文件</p>
<p><br>$LIBVERSION指令添加一个二层扩展(在原扩展名之后)到输出文件名中。例如使用<br>{$LIBVERSION '-2.1.3'}<br>在something.pas中将产生<br>libsomething.dll.2.1.3 文件</p>
<p><br>Delphi编译指令详解(8)——定义指令</p>
<p>Type Conditional compilation //条件指令<br>Syntax {$DEFINE name}</p>
<p><br>根据一个给定的名字设计一个Delphi条件符号。这个符号需要在此模块中已经声明和验证,<br>或者在{$UNDEF name}中已经出现。如果 此名字已经定义则{$DEFINE name}将失效。</p>
<p><br>Delphi编译指令详解(9)——描述指令</p>
<p>Type Parameter //参数<br>Syntax {$DESCRIPTION 'text'}<br>Scope Global //局部</p>
<p>说明</p>
<p><br>$D指令插入你指定的模块文件中,当它应放在可执行文件,Dll文件,或包的前面。<br>一般来说指明的是名字、版本号和版权,但是你也可以指定你需要指定的内容。<br>例如:<br>{$D 'My Application version 12.5'}</p>
<p><br>字符串不能超过256个字节。此描述对于最终用户一般是看不到的。标志你的执行文件,<br>通过描述性文字,版本和版权信息,将对你的最终用户有意。</p>
<p>提示:此文本描述必须包含在引用中。</p>
<p><br>Delphi编译指令详解(10)——ELSE指令</p>
<p>Type Conditional compilation //条件指令<br>Syntax {$ELSE}</p>
<p>说明:</p>
<p>此指令位于{$IFxxx}之后,{$ENDIF} or {$IFEND}之前,是编译与忽略源代码的界线。</p>
<p>&nbsp;</p>
<p>分  类:不常用<br>语  种:简体中文<br>编辑器:文档<br>平  台:Win9x,Win2k/NT,WinXP<br>作品源代码: -<br>软件或演示: -<br>代码大小: -<br>软件大小: -</p>
<p>无图片</p>
<p>《Delphi下深入Windows核心编程》(附录A Delphi编译指令说明)<br>摘抄人:麻子 qq:71892967</p>
<p>Delphi快速高小的编译器主要来自Object PASCAL的严谨,使用Delphi随时都在与编译器交流,大部分情况下不需要干涉编译器的运行,但是有时也需要对编译器进行必要的设置。</p>
<p>**********************************************************************************************</p>
<p>A.1 使用编译设置对话框</p>
<p>编译器的编译指令是用于指定编译器对项目编译过程的动作和行为。可以通过-&gt;-&gt;选项页进行设置,绝大部分的编译环境都可以通过这一对话框进行调整,他包含了对代码、语法、调试信息等的设置。</p>
<p>1.代码设置(Code Generation)</p>
<p>Optimization: 代码优化开关</p>
<p>Aligned record fields: 字对齐数据。这个编译指令能够在变量和类型化常量的字节对齐和字对齐之间进行切换,其作用是全局的。</p>
<p>Stack frames: Windows 堆栈帧。其作用域是局部的,他使编译器成为远端过程和函数生成特定的开头和结尾代码。这个指令用于Windows 3.0的实模式,对所有Delphi应用程序他应该是关闭的。</p>
<p>Pentium-safe FDIV: Pentium安全FDIV检查。此指令能够在Pentium处理器中指定编译器是否创建能够检测和使用有缺陷的浮点除法指令的代码。</p>
<p>2.运行期错误(Runtime Errors)</p>
<p>Range Checking: 范围检查。这个指令的作用范围是局部的,用于控制范围检验代码的生成。</p>
<p>I/O Checking: I/O检查。这个指令的作用域为局部,用来生成对一个文件的输入和输出过程和调用结果进行检查的代码。一般应该使其功能打开。</p>
<p>Overflos Checking: 溢出检查。其作用是生成对算术溢出检查的代码。</p>
<p>3.语法设置(Syntax Optings)</p>
<p>Strict Var- strings: 静态变量串开关。用来控制对以变量参数形式传递的串的类型检查。</p>
<p>Comlete Boolean Eval: 完全布尔表达量判定。用于执行强制的完整表达式判定。完全布尔量判定,有可能导致系统崩溃,所以一般不使用他。</p>
<p>Extended Syntax: 扩展语法开关。他能允许或禁止Delphi的扩展语法。</p>
<p>Typed @ Operator: 类型化的@运算符检查。应用于局部的变量引用上,控制@运算符返回的指针值类型。</p>
<p>Open Parameters: 开放参数开关。用于控制使用String关键字声明的变量参数。开放参数允许将大小不一的串变量传递到同一个过程或函数中,一般在汇编中会使用到。</p>
<p>Huge Strings: 字符串类型开关。用于控制Ansistring和Shortstring类型的切换。当打开时符合Ansistring,关闭时符合Shortstring。</p>
<p>Assignable Typed Constants: 可分配类型常量。用于向下与Delphi 1.0兼容。</p>
<p>4.调试(Debugging)</p>
<p>Debug Information: 调试信息开关。用于设置是否把调试信息写入以编译的单元文件(.dcu)。</p>
<p>Local Symbols: 局部符号开关。允许或禁止局部符号信息的创建。</p>
<p>Reference Info/Definitions Only: 符号信息开关。用于允许或禁止由Delohi的对象浏览器使用的符号引用信息的生成。</p>
<p>Assertions: 用于控制局部代码的属性。</p>
<p>Use Debug DCUs: 使用或禁止VCL的DCU文件调试。</p>
<p>5.信息(Messages)</p>
<p>Show Hints: 显示暗示。</p>
<p>Show Warnings: 显示警告。</p>
<p>**********************************************************************************************</p>
<p>A.2 使用编译指令</p>
<p>除了使用编译设置对话框对编译器进行设置外,还可以通过编译指令来对编译器进行设置。<br>对于局部的编译器设置,只有使用编译指令来完成。</p>
<p>对于开关编译指令,通过在编译指令后加入指示开关状态的加号和减号来控制编译器。例如:</p>
<p>{$B+} : 打开完全布尔量检查。<br>{$Q-} : 关闭溢出检查。</p>
<p>通常,编译指令的作用域是在编译指令后的代码部分,而对于全程的编译指令应该防在单元接口部分的开头。<br>编译设置对话框的设置都有与之对应的编译指令用于在代码中对编译器进行设置,如下表所示。</p>
<p>设置项 编译指令</p>
<p>Optimizations {$O}</p>
<p>Aligned record fields {$A}</p>
<p>Stack frames {$W}</p>
<p>Pentium-safe FDIV {$U}</p>
<p>Range Checking {$R}</p>
<p>I/O Checking {$I}</p>
<p>Overflow Checking {$Q}</p>
<p>Strict Var-strings {$V}</p>
<p>Comlete Boolean Eval {$B}</p>
<p>Extended Syntax {$X}</p>
<p>Typed @ Operator {$T}</p>
<p>Open Parameters {$P}</p>
<p>Huge Strings {$H}</p>
<p>Assertions typed constants {$J}</p>
<p>Debug information {$D}</p>
<p>Local sysnbols {$L}</p>
<p>在这些编译指令以外还有一些非常有用的编译指令。<br>$R Filename : 这个编译指令是最为常用的编译指令,他是资源文件编译指令,用于指定连接到执行文件和库的资源文件,例如在工程文件(.dpr)中会有{$R *.RES}的编译指令,表明把后缀为 .RES的与工程文件同名的资源文件连接入执行文件,也可以指定一个资源文件,资源文件的使用对于编写Windows程序来说是很重要的基础。</p>
<p>$I Filename :这个编译指令功能类似于C语言的#Include , 用于指定编译时包括的文件。</p>
<p>**********************************************************************************************</p>
<p>A.3 使用条件编译指令</p>
<p>条件编译指令是非常重要的编译指令,他控制着在不同条件下(例如,不同的操作系统)产生不同的代码。条件编译指令是包含在<br>注释括号之内的,如下表所示。</p>
<p>条件编译指令 含义</p>
<p>$DEFINE 用于定义一个条件符号,一旦定义,条件符号就为真</p>
<p>$ELSE 与$IFDEF配合使用,如果$IFDEF条件为假,则只对源文件$ELSE后一小部分进行编译</p>
<p>$ENDIF 结束一个以$IF开始的条件段</p>
<p>$IFDEF 对条件符号进行判断,为真则编译源文件</p>
<p>$IFNDEF 对条件符号进行判断,为假则编译源文件</p>
<p>$IFOPT 根据编译开关状态,对源文件编译</p>
<p>$UNDEF 撤消以前的条件符号定义</p>
<p>这些条件编译指令是非常有用的。例如,可以通过开关的状态来控制编译:</p>
<p>{IFOPT R+}<br>showmessage('Compiled with range-checking');<br>{$ENDIF}</p>
<p>也可以通过定义条件符号来控制编译:</p>
<p>{$Define s}<br>……<br>{$ifdef s}<br>showmessage('yes');<br>{$else}<br>showmessage('no');<br>{$endif}</p>
<p>他的编译结果是显示'yes',但是如果省去{$Define s}则显示'no'。<br>在Delphi中已经预定义了一些关键的条件符号,如下表所示。</p>
<p>条件符号 含义</p>
<p>VERxx 编译器版本,XX表示版本,例如:Delphi 1.0 的编译器版本为80、Delphi 5.0 的编译器版本为130</p>
<p>WIN32 是否WIN32的运行环境(Windows 95.98/NT/2000)</p>
<p>CPU386 是否Intel386以上的处理器</p>
<p>CONSOLE 是否控制台程序</p>
<p>Delphi的编译器指令除了以上的指令外还有一些,不过最为常用的指令已经全部介绍完了。对于普通的程序,Delphi是不需要编程者去添加编译器指令的,Delphi已经自动完成,但是要得到高品质的应用程序或者有特殊的要求的程序就必须熟悉Delphi的编译指令。Delphi不仅有最快的编译器而且编译器的功能也非常强大。</p>
<p>{$IFDEF WIN32} -- 這可不是註解喔!<br>對於Delphi來說﹐左右大括號之間的內容是註解﹐然而「{$」<br>(左括號後緊接著貨幣符號)對於Compiler(編譯器)而言並不是註解﹐<br>而是寫給Compiler看的特別指示。<br>應用時機與場合<br>Delphi中有許許多多的Compiler Directives(編譯器指令)﹐<br>這些編譯指令對於我們的程式發展有何影響呢? 它們又能幫我們什麼忙呢?<br>Compiler Directive 對程式開發的影響與助益, 可以從以下幾個方向來討論:<br>Ø 協助除錯<br>Ø 版本分類<br>Ø 程式的重用與管理<br>Ø 設定統一的執行環境<br>協助除錯<br>穩健熟練的程式設計師經常會在開發應用系統的過程中﹐特別加入<br>一些除錯程式或者回饋驗算的程序﹐這些除錯程式對於軟體品質的<br>提升有極其正面的功能。然而開發完成的正式版本中如果不需要這<br>些額外的程式的話﹐要想在一堆程式中找出哪些是除錯用的程式並加以<br>刪除或設定為註解﹐不僅累人﹐而且容易出錯﹐況且日後維護時這些除錯程式還用得著。<br>此時如果能夠應用像是$IFDEF的Compiler Directives ﹐就可以輕易的<br>指示Delphi要/不要將某一段程式編進執行檔中。<br>同時﹐Compiler本身也提供了一些錯誤檢查的開關﹐<br>可以預先對程式中可能的問題提醒程式設計師注意﹐同樣有助於撰寫正確的程式。<br>版本分類<br>除了上述的除錯版本/正式版本的分類之外﹐對於像是「試用版」「普及版」<br>「專業版」的版本分類﹐也可以經由Compiler Directive的使用﹐為最後的<br>產品設定不同的使用權限。其他諸如「中文版」「日文版」「國際標準版」<br>等全球版本管理方面﹐同樣也可以視需要指示Delphi特別連結哪些資源檔或<br>者是採用哪些適當的程式。以上的兩則例子中﹐各版本間只需共用同一份程式碼即可。<br>Delphi 1.0 與 Delphi 2.0有許多不同之處﹐元件資源檔(.DCR)即是其中<br>一例﹐兩者的檔案格式並不相容﹐在您讀過本文之後﹐相信可以寫出這樣的程式﹐<br>指示Delphi在不同的版本採用適當的資源檔以利於元件的安裝。<br>{$IFDEF WIN32}<br>{$R XXX32.DCR}<br>{$ELSE}<br>{$R XXXX16.DCR}<br>{$EDNIF}<br>程式的重用與管理<br>經過前文的討論後﹐相信你已經不難看出Compiler Directives在程式管理上的應用價值。<br>對於原始程式的重用與管理﹐也是Compiler Directives 使得上力的地方. 舉例來說:<br>Pascal-Style字串是Delphi 1.0與 Delphi 2.0之間的明顯差異﹐除了原先的短字串之外﹐<br>Delphi 2.0之後還多了更為方便使用的長字串﹐同時﹐系統也額外提供了像是 Trim()<br>這樣的字串處理函式。假如您有一個字串處理單元必須要同時應用於Delphi 1.0 與<br>2.0的專案時﹐編譯指示器可以幫你的忙。<br>此外﹐透過像是{$I xxxx} 這樣的 Compiler Directives﹐我們也可以適當的含入某些<br>程式, 同樣有助於切割組合我們的程式或編譯設定。<br>設定一致的執行環境<br>專案小組的成員間﹐必須有共同的環境設定﹐我很難預料一個小組成員間彼此有不同的<br>{$B}{$H}{$X}設定﹐最後子系統在併入主程式時會發生什麼事。<br>此外, 當您寫好一個元件或單元需要交予第三者使用時, 使用編譯指示器也可以保證元<br>件使用者與您有相同的編譯環境。<br>使用Compiler Directives<br>指令語法<br>Compiler Directives從外表看起來與註解頗為類似, 與註解不同的是:<br>Compiler Directives的語法格式都是以「{$」開始, 不空格緊接一個名<br>稱(或一個字母)表明給Compiler的特別指示, 再加上其他的開關或參數內容,<br>最後以右大括號作為指令的結束, 例如:<br>{$B+}<br>{$R-}<br>{$R MyCursor.res}<br>同時, 就如同Pascal的變數名稱與保留字一樣, Compiler Directives也是<br>不區分大小寫的。<br>從指令的語法格式來說Compiler Directives﹐可以進一步分類成以下三種格式:<br>Ø 開關指令(Switch directives)<br>這類指令都是單一字母以不空格的方式連接「+」或「-」符號; 或者是開關名<br>稱以一個空格後連接「ON」或「OFF」來表示作用/關閉某一個編譯指示開關。例如:<br>{$A+}<br>{$ALIGN ON}<br>開關型的編譯指令不一定要分行寫, 它們可以組合在同一個編譯指示的註解符號之間,<br>但必須以逗號連接, 而且中間不可以有空格, 例如:<br>{$B+,H+,T-,J+}</p>
<p>游標停留在程式編輯器的任一位置時按下Ctrl+O O, 完整的Compiler Directives<br>將會全部列於Unit的最上方。<br>Ø 參數指令(Parameter directives)<br>有些Compiler Directives需要在編譯名稱後面連接自定的參數(檔案名稱或指定的記<br>憶體大小), 例如: {$R MyCursor.res}, 即在指示Delphi在編譯連結時,<br>含入「MyCursor.res」這個資源檔。<br>Ø 條件指令(Conditional directives)<br>指示Compiler在編譯的過程中, 按我們設定的條件, 選擇性的採用/排除不同區域的<br>程式碼。<br>以下是一個條件編譯的例子, 第一與第三列是寫給Compiler看的,指示<br>Compiler在 __DEBUG這個條件名稱完成定義的情況才編譯ShowMessage()這列程式;<br>反之, 如果 __DEBUG 當時沒有定義的話, 這段程式幾乎與註解無異, Compiler對<br>它將視而不見。<br>{$IFDEF __DEBUG}<br>ShowMessage(IntToStr(i));<br>{$ENDIF}<br>如何從IDE改變Compiler directives設定<br>從Delphi的IDE程式整合發展環境, 我們很方便的就可以修改各個compiler directives的<br>設定, 方法是:<br>從Delphi IDE主選單: Project/Options/Compiler, 直接核選/取消各個CheckBox。<br>值得注意的是, 改變一個專案的Compiler directives並不會影響其他的專案, 換言之,<br>各個專案都保有自己一套編譯指示。<br>假如您希望其他的專案也採用相同一套的Compiler directives, 在上述Project Options<br>對話盒的左下方有一個「Default」選項, 選取這個CheckBox之後, 雖然對於既有的專案<br>沒有作用, 但未來新的專案都將可以採用這組設定作為預設值。<br>將Compiler directives寫入程式<br>透過Delphi的整合環境設定Compiler directives的確十分簡便, 但是許多情況下我們<br>仍然需要將Compiler directive直接加到程式中。至少有兩個原因支持我們這麼作:<br>Ø 局部控制編譯條件<br>在Project/Options/Compiler中所作的設定, 影響所及是整個專案, 如果某一段程式<br>要特別使用不同的編譯設定, 就必須直接將編譯指示加到程式中。<br>下列這段取自Online Help的程式範例, 即應用了{$I}編譯指令局部控制在發生I/O錯誤<br>時不要舉發例外訊息, 這樣, 我們就可以編譯出一支在這段程式區域中不會產生I/O例外<br>訊息的檔案偵測函數。<br>function FileExists(FileName: string): Boolean;<br>var<br>&nbsp; F: file;<br>begin<br>&nbsp; {$I-}<br>&nbsp; AssignFile(F, FileName);<br>&nbsp; FileMode := 0;&nbsp; ( Set file access to read only }<br>&nbsp; Reset(F);<br>&nbsp; CloseFile(F);<br>&nbsp; {$I+}<br>&nbsp; FileExists := (IOResult = 0) and (FileName &lt;&gt; '');<br>end;&nbsp; { FileExists }<br>Ø 程式的可攜性<br>我們都可能會用到其他公司或個人創作的unit或component, 也可能分享程式給其他人,<br>換句話說, 單元或程式可能會在不同的機器上編譯, 直接將Compiler directives加入<br>程式, 不僅可以免去程式使用前需要特別更改IDE的麻煩, 更重要的是解決了各個單元<br>間要求不同編譯環境的歧異。<br>注意事項<br>Compiler directives的作用與影響範圍<br>如同變數的可見範圍與生命週期, 在我們使用 Compiler Directives 時也必須注意各個<br>Compiler Directives 的作用範圍.<br>Compiler Directives的作用範圍可分為以下兩種:<br>Ø 全域的<br>全域的Compiler Directives, 影響所及是整個專案; 我們稍早前提到經由Delphi<br>IDE改變Compiler directives的方式就屬於全域的設定。<br>Ø 區域的<br>而區域的Compiler Directives 影響所及只從Compiler Directives 改變的那一行開始,<br>直到該程式單元(Unit)的結束或另一個相同的Compiler Directives 為止,<br>對其他的程式單元並沒有影響。<br>也就是說, 如果在unit中特別加入Compiler directives, Compiler會優先採用區域<br>的設定, 然後才是屬於專案層級的全域設定。<br>值得一提的是, 在程式中直接加入Compiler directives的最大作用範圍也只限於當時<br>那個單元而已, 對其他單元並沒有任何影響, 即使是以uses參考也是一樣。也就是說,<br>我們可以透過uses參考其他unit公開的變數與函式, 但是各個unit的編譯指令並不會互<br>相參考。<br>這項獨立的性質, 使得unit之間編譯環境的設定與關係變得十分簡潔, 例如Delphi<br>2.0的VCL都是在{$H+}的情況下編譯的, 因此, VCL中的字串都是以長字串的型態編譯<br>而成的, 有了這項編譯指令獨立的特性, 不論我們Prject中的設定為何, 這些在VCL中<br>定義過的字串都是長字串。我們的Project也不會因為uses了VCL中的unit而改變了自己<br>的設定。<br>因此, 在我們移交程式到網路上時, 大可以放心的在程式中加入必要的Compiler<br>directives, 別擔心, 即使別的unit以uses參考了我們的程式, 也不影響它自己原來<br>的設定。<br>如果我們自行以{$DEFINE _DEBUGVERSION}($DEFINE在稍後的個別指令介紹中將有說明)<br>定義了一個條件符號, 這個新的條件符號也是區域的, 換句話說, 它只從定義的那一<br>個單元的那一列之後才成立, 當然, 也只對目前這個單元有效.<br>由於自訂的條件符號只有區域的作用, 如果有好幾個程式單元都需要參考到某一個條<br>件符號, 怎麼辦呢? 嗯! 在每一個程式單元開頭處中都加上編譯指示是最直接的方式,<br>可是略嫌麻煩, 特別是編譯指示有變時, 要一一修正各個單元的設定內容, 很容易因為<br>疏忽而出錯。<br>比較簡易可行的作法是從Delphi IDE整合發展環境的主選單-Project / Options /<br>Directories/Conditional 的 Conditionals 中填入條件名稱。這樣, 相對於專案的<br>各個unit而言, 就有了一個全域的條件符號。<br>或者, 您也可以參考本文對於{$I}這個Compiler Directive的說明。 我在那裏指出了<br>另一個彈性的解決方式。<br>修改過編譯指令後, 建議Build All過一次程式<br>請試一試這個程式:<br>procedure TForm1.Button1Click(Sender: TObject);<br>begin<br>// ifopt是用來偵測某一個編譯開關的作用狀態<br>{$ifopt H+}<br>&nbsp; ShowMessage('H+');<br>{$else}<br>&nbsp; ShowMessage('H-');<br>{$endif}<br>end;<br>在我們執行上述程式時, 在Delphi預設的是$H+時, ShowMessage()會在畫面上會顯示<br>「H+」, 執行過後, 讓程式與form的內容與位置保留不變, 單純的從主選單:<br>Project/Options/Compiler, 將Huge Strings的核對方塊清除($H-),<br>然後按下F9執行, 咦! 怎麼還是看到「H+」?!<br>那是因為Delphi只會在unit內容經過異動後才會重新將.PAS編譯成.DCU,<br>在我們的例子中, 程式並沒有變動, .DCU當然也沒有重新產生, 最後.EXE的這個部分<br>自然也是沒什麼變化。<br>所以, 要解決這個問題, 只要以Delphi IDE主選單Project/Build All指示Delphi<br>重新編譯全部的程式即可。因此, 如果您從Delphi IDE修改過Compiler Directives後,<br>記得要Build All喔!<br>不應該用來作為程式執行流程控制<br>在程式中, 我們可以使用if敘述, 根據執行當時的情況控制程式執行時的流程,<br>但我們不可以用{$IFDEF}來作同樣的事, 為什麼? 從上述的說明, 相信您不難發現,<br>Compiler directives會對最後.EXE的內容發生直接的影響, 應用像是{$IFDEF}指示<br>Compiler的結果, 幾乎可以視同授權Compiler在編譯的那個時候自動選用/捨棄程式<br>到.DCU, .EXE中, 換句話說, 在程式編譯完成時, 會執行到那一段程式已成定局了,<br>我們自然不能用它來作程式流程的控制。<br>條件編譯的巢套最多可以16層<br>在使用{$IFDEF}…{$ENDIF}條件編譯我們的程式時, 一個{$IFDEF}中可以再包含另一<br>個{$IFDEF}, 但深度最多只能16層, 雖然是個限制, 但以正常的情形來說, 這應該已<br>經足夠了。<br>有些Compiler directives不應寫在Unit中<br>對於像是{$MINSTACKSIZE}{$MAXSTACKSIZE}管理堆疊大小, 或者像是{$APPTYE}<br>指示程式編譯成圖形/文字模式的Compiler directives, 只能寫在.DPR中, 寫在Unit<br>中是沒有效果的。<br>建議事項<br>確定您瞭解指令的影響<br>由於編譯指令的影響是如此直接與深遠, 在修改與應用某一個Compiler directive時,<br>請確定您已經了解其含意與影響。<br>打開全部的偵錯開關<br>Delphi有關偵錯的Compiler directives如下:<br>Ø $HINTS ON<br>Ø $D+<br>Ø $L+<br>Ø $Q+<br>Ø $R+<br>Ø $WARNINGS ON<br>各指令的用法您可以參閱本章稍後對個別指令的說明, 全部打開這些開關吧!<br>這樣不僅讓您可以使用Delphi IDE的除錯器, 對於程式編譯與執行過程中的問題,<br>Delphi也會適時的反應, 有助於寫作正確的程式。<br>此處有一個迷思有待澄清—「加入Dubug資訊會不會讓執行檔變大變慢啊?」, 不一定。<br>對於們像是$D+, $L+, $HINTS ON這些開關, 打開後, Delphi在編譯時的確會額外加入<br>一些除錯資訊, 使得.DCU的檔案變大, 對於.EXE的檔案大小並沒有影響; 同時,<br>程式的執行速度也沒有改變, 還可以應用IDE的除錯器trace我們的程式, 值得應用。<br>對於像是$Q, $R等Compiler directive, 的確會影響執行檔的大小與速度, 然而這並<br>不動搖我們在研發期間使用它們的決定, 請想想看, 值得為這一點點的速度放棄程式<br>的正確性嗎? 當然, 程式開發完成後, 正式出貨的版本, 可以關閉這兩個開關。</p>
<p>如果您寫好了一個元件, 而且只預備提供.DCU, 由於沒有.PAS可供Delphi IDE的Debugger<br>追蹤程式, 除錯開關反而應該在元件脫手前關閉並重新編譯.DCU, 否則會引起使用者那<br>邊找不到檔案的例外訊息。<br>善用{$I}<br>{$I FileName}是一個非常有用的Compiler directive.應用這個指令, 我們可以彈性的<br>管理Compiler directive的設定。<br>條件名稱請加入前置字元<br>不知道您有沒有這個疑問 -- 如果用{$DEFINE}定義的條件名稱與變數名稱相同時會發<br>生什麼事?<br>procedure TForm1.Button1Click(Sender: TObject);<br>var<br>&nbsp; TEST: integer;<br>begin<br>{$DEFINE TEST}<br>{$IFDEF TEST}<br>&nbsp;&nbsp; ShowMessage('Test');<br>{$ENDIF}<br>end;<br>以上的程式編譯與執行都沒有問題, 但條件名稱與變數名稱重覆畢意容易讓人混淆, 因此,<br>假如能適當的為編譯條件名稱之前加上諸如底線(_TEST), 程式會比較容易閱讀。<br>設定一致的編譯環境<br>在您了解了Compiler Directives之後, 請立即開始著手修改您IDE中有關編譯指示<br>的各個開關並且設為Default, 這樣, 日後您的專案乃至整個研發小組都將擁有<br>共同一致的編譯環境, 對於寫出來的程式會以何種方式編譯連結都了然於胸,<br>直接有助於子系統順利併入主系統中。<br>個別指令說明<br>有了之前對於Compiler directives的觀念之後, 接下來的這一節我將一一<br>介紹幾個常用的Compiler Directive的用法與注意事項, 您可以從這一<br>節中學到更多有關Compiler directives的知識與使用細節。<br>{$A+} 欄位對齊<br>在{$A+}(預設值)的情形下, 如果沒有使用 packed 修飾詞宣告的 record<br>型態, 其欄位會以CPU可以有效存取的方式向 1. 2. 4 等邊界對齊,<br>以獲取最佳的存取速度。以下列的程式示例來說:<br>{$A+}<br>type<br>&nbsp; MyRecord = record<br>&nbsp;&nbsp;&nbsp; ByteField: byte;<br>&nbsp;&nbsp;&nbsp; IntegerField: integer;<br>&nbsp; end;<br>…<br>procedure TForm1.Button1Click(Sender: TObject);<br>begin<br>&nbsp; ShowMessage(IntToStr(SizeOf(MyRecord)));<br>end;<br>ShowMessage在{$A+}時顯示的結果是:「8」; 倘若是{$A-}, 那所得的結果是「5」,<br>按理說, Byte應該只要一個byte就足夠了, 但是考慮到硬體的執行特性,<br>經過對齊後的record會有比較好的執行速度。<br>有關這個Compiler Directive要注意的事項是: 不管{$A}的開關是ON或OFF,<br>使用packed修飾過的記錄宣告, 是一定不會對齊的. 例如:<br>&nbsp; MyRecord = packed record // 不會對齊的記錄宣告方式<br>{$APPTYPE GDI} 應用程式型態<br>一般的情形下, Delphi會以{$APPTYPE GUI}的方式產生一個圖形的使用者介面程式,<br>如果您需要產生一個文字螢幕模式的程式, 那可以經由:<br>Ø 在.DPR中加入{$APPTYPE CONSOLE}<br>Ø 從主選單: Project/Options/Linker/EXE and DLL Options, 核取<br>「Generate Console Application」Check Box。<br>其他有關這個Compiler Directive的注意事項有:<br>Ø $APPTYPE不能應用在DLL的專案或單一的程式單元(Unit), 它只對.EXE有意義。<br>而且只有寫在.DPR中才有作用。<br>Ø 我們可以應用System程式單元中的IsConsole函數在程式執行時偵測應用程式<br>的類型。<br>Ø 參閱Object Pascal手冊第十三章可以知道更多有關Console Mode<br>Application的資訊。<br>{$B-} 布林評估<br>請看以下的程式:<br>&nbsp; if (Length(sCheckedDateString) &lt;&gt; 8)<br>&nbsp;&nbsp;&nbsp; or EmptyStr(sCheckedDateString)<br>&nbsp;&nbsp;&nbsp; or (sCheckedDateString = '&nbsp; .&nbsp; .&nbsp; ')<br>&nbsp;&nbsp;&nbsp; or (sCheckedDateString = '&nbsp; /&nbsp; /&nbsp; ') then<br>&nbsp; begin<br>&nbsp;&nbsp;&nbsp; Result := True;<br>&nbsp;&nbsp;&nbsp; Exit;<br>&nbsp; end;<br>假如sCheckedDateString的字串內容是「85/12/241」(長度9)的話, 以上的if述句,<br>其實在第一個邏輯判斷時就已經知道結果了, 即使不看後來的邏輯運算結果也知道<br>整個式子會是真值。<br>假如您希望對整個邏輯運算式進行完整的評估 -- 儘管結果已知, 後來的邏輯運算<br>也不影響整個的結果時仍要全部評估過, 請將這個Compiler directives設為{$B+},<br>反之, 請設為{$B-}, 系統的預設值是{$B-}。<br>{$D+} 除錯資訊<br>當程式以{$D+}(預設值)編譯時, 我們可以用Delphi整合發展境境的Debugger設定<br>中斷點, 也可以使用Trace Into或Trace Info追蹤程式的執行過程, 值得注意的是,<br>以{$D+}編譯的程式, 執行的速度並不會受到影響, 只不過編譯過的DCU的檔案長度會<br>加大, 但EXE檔的大小不變。<br>{$DEFINE條件名稱} 定義條件名稱<br>隨著您對Compiler Directives的瞭解與應用程度的加深, 您會發現這是一個非常實<br>用的編譯指示。<br>經常, 我們會因為除錯需要﹑區別不同版本等緣故, 希望選擇性的採用或排除某一<br>段程式, 這個時候, 我們就可以先以$DEFINE定義好一個條件名稱(Conditional name),<br>然後配合{$IFDEF條件名稱}…{$ELSE}…{$ENDIF}指示編譯器按指定的條件名稱之有無<br>來選擇需要編譯的程式。以下列的程式片斷來說:<br>{$DEFINE _ProVersion}<br>…<br>procedure TForm1.Button1Click(Sender: TObject);<br>begin<br>{$IFDEF _Proversion}<br>&nbsp;&nbsp; frmPrint.ShowModal;&nbsp; // A<br>{$ELSE}<br>&nbsp;&nbsp; ShowMessage('很抱歉, 試用版不提供列印功能');<br>{$ENDIF}<br>end;<br>編譯器將會選擇編譯上述A的那列程式, 日後, 如果我們需要編譯「簡易版」<br>的程式版本時, 只要:<br>Ø 將{$DEFINE _ProVersion}那列整個刪掉。<br>Ø 或者, 將{$DEFINE _ProVersion}改成{-$DEFINE _ProVersion},<br>讓它變成普通的註解<br>Ø 或者, 在{$DEFINE _ProVersion}的下一列加上{$UNDEF _ProVersion},<br>解除_ProVersion這個條件名稱的定義。<br>這樣, 由於_ProVersion這個條件名稱未定義的緣故, Compiler就只會選擇<br>{$ELSE}下的那段程式, 重新編譯一次, 不需費太多力氣, 很容易的就可以製作出<br>「簡易版」了, 省去了要同時維護兩份程式的麻煩。<br>使用$DEFINE時的其他注意事項如下:<br>Ø 以{$DEFINE}定義的條件名稱都是區域的。換句話說, 它的作用範圍只在<br>當時所在的單元才有效, 即使定義在unit的interface, 由其他的unit以uses參考也沒有效,<br>仍然只有在目前的unit有作用。<br>Ø 此外, 它的作用範圍是從定義起, 到unit結尾或者以{$UNDEF}解除為止。<br>Ø 如果程式單元中已經用{$DEFINE}定義了一個條件名稱, 而且也沒有用<br>{$UNDEF}解除定義, 重新{$DEFINE}一個同樣名稱並沒有作用, 換句話說, 它們是同一個.<br>Ø 假如需要一個全域的條件名稱, 您可以:主選單: Project / Options / Directories/<br>Conditional 的 Conditionals 中填入條件名稱。<br>Ø 以下的標準條件名稱, 是Delphi 2.0已經預先預備好的, 我們可以直接引用,<br>同時, 它們都是全域的, 任何Unit都可以參照得到。<br>Ø VER90: Delphi Object Pascal的版本編號。90表示9.0版, 日後若出現9.5<br>版時, 也會有VER95的定義。<br>Ø WIN32: 指出目前是在Win32(95, NT)作業環境<br>Ø CUP386: 採用386(含)以上的CPU時, 系統會提供本條件名稱。<br>Ø CONSOLE: 此符號會於應用程式是在螢幕模式下編譯時才定義。<br>{$DESCRIPTION&nbsp; 描述內容}<br>應用{$DESCRIPTION}可以指定加入一段文字到.EXE或.DLL表頭的模組描述進入點<br>(module description entry)中﹐通常我們會用這個Compiler Directive加入應<br>用程式的名稱與版本編號到.EXE中。例如:<br>{$DESCRIPTION Dchat Version 1.0}<br>{$X+}&nbsp; 擴充語法<br>這是為了與之前的Pascal版本前向相容的編譯指令, 雖然設定這個開關型的指令仍有<br>作用, 但筆者建議您大可保留系統的預設值{$X+}, 在{$X+}下:<br>Ø 不需要非得準備一個變數接受函數的傳回值, 換句話說, 函數的傳回值可以<br>捨棄, 此時, 就可以像是呼叫程序一樣, 很方便的呼叫函數。<br>Ø 支援Pchar型態與零基的字元陣列作為C語言以Null結尾的字串。<br>{$HINTS OFF}&nbsp; 提示訊息<br>打關{$HINTS}開關後, Compiler會提示程式設計師注意以下的情況:<br>Ø 變數定義了卻沒有使用<br>Ø 程式流程中不會執行的for或while迴圈<br>Ø 只有存入沒有取用的指定敘述。意思是說, 指定資料到某一個變數之後,<br>卻沒有任何的程式參考取用這個變數值。<br>{$HINTS ON}<br>procedure MyTest;<br>const _False = False;<br>var<br>&nbsp; I, J: integer;<br>begin<br>&nbsp; if _False then<br>&nbsp;&nbsp;&nbsp; for I := 1 to 3 do ;<br>&nbsp; J := 3;<br>end;<br>{$HINTS OFF}<br>由於程式簡單, 在兩個$HINTS中間的程式, 我們不難看出:<br>Ø for迴圈不會執行到, I變數也因此不曾用過<br>Ø J := 3寫了等於白寫<br>但在程式越寫越長而日趨複雓時, 藉由{$HINTS ON}的協助, 比較容易察覺出程式的毛病。<br>{$IFDEF} {$IFNDEF}<br>請參閱{$DEFINE}的說明, 在此補充說明{$IFNDEF}, 以下列程式來說, 即在指示Compiler<br>在_Test未定義時, 條件編譯ShowMessage()那列程式:<br>{$IFNDEF _TEST}<br>&nbsp; ShowMessage('_TEST not define');<br>{$ENDIF}<br>換言之, {$IFNDEF}相當於{$IFDEF}的{$ELSE}部分。<br>{$IFOPT 開關}<br>到底{$B}是開著或關著呢? 如果我們想要指示Compiler按照某一個編譯開關當時的狀態作<br>我們指定的事, 應該該怎麼做呢? 這時, {$IFOPT}就派得上用場了。例如:<br>{$R+}<br>{$Q-}&nbsp; // 特別指定為Q-<br>{$IFOPT R+} // 如果 Range Check 是開啟的話<br>&nbsp; ShowMessage('程式是在 Range Check 開啟狀態下編譯的');<br>&nbsp; // 這個 Q+ 也會在 IFOPT R+ 成立時才通知 Compiler<br>&nbsp; {$Q+}<br>{$ENDIF}<br>{$IFOPT Q+}<br>&nbsp; ShowMessage('Q 也變成開啟狀態了');<br>{$ENDIF}<br>ShowMessage() 與 {$Q+}會在$R+ 的情形下才編譯, 因此, 雖然我們事前特別指示為{<br>$Q-}, 第二個的ShowMessage()在程式執行時也可以看到「Q 也變成開啟狀態了」。<br>{$IMAGEBASE檔案基礎位址}<br>這個Compiler directive用來指示.EXE或.DLL載入時的預設位址。例如: {$IMAGEBASE<br>$00400000}。如果指定載入的位址空間之前已經有其他模組佔用了, Windows會為.EXE重<br>新配置一個新的載入位址。對於.DLL來說, 如果可以成功配置到我們寫在{$IMAGEBASE}<br>的位址, 由於不需要重新配置記憶體位址, 不僅載入的速度較快, 如果有其他程式也參<br>照到這個DLL的話, 也可以減少載入時間與記憶體的消耗。<br>使用這個Compiler directive時需要注意的事項有:<br>Ø 指定的敘述必須是一個大於$00010000的32位元整數數值, 同時, 較低位置的<br>16個位元必須是零。<br>Ø DLL的建議位址範圍從$40000000到$7FFFFFFF, 該範圍的位址可以同時適用於<br>Windows 95與Windows NT。<br>{$I檔案名稱} 含入檔案<br>以Delphi IDE修改Compiler directives的確相當方便, 但往往我們仍然需要將Compiler<br>directives直接加入程式中, 可是當我們這樣作之後不用多久, 就會發現要一一重新<br>修改各個單元中的這些Compiler directives時, 實在是既無聊而又容易出錯的工作。<br>這時候, 假如您一開始就採用{$I檔案名稱}, 整件事就會變得很簡單。怎麼做呢?<br>讓我用一個例子告訴您 --<br>Ø 先用一般的文書編輯器建好一個MySet.inc的普通文字檔案, 內容為:<br>{$H+}<br>{$DEFINE _Proversion}<br>Ø 在我們的程式中, 加入一列{$I MySet.inc}, 例如:<br>unit Unit1;<br>{$I MySet.inc}</p>
<p>interface<br>…<br>implementation</p>
<p>{$R *.DFM}</p>
<p>procedure TForm1.Button1Click(Sender: TObject);<br>begin<br>{$IFDEF _ProVersion}<br>&nbsp; ShowMessage('專業版');<br>{$Else}<br>&nbsp; ShowMessage('只有專業版才有此功能');<br>{$ENDIF}<br>end;<br>…<br>這是副程式的觀念嘛! 沒錯, 就是這麼簡單而已, 以後如果有任何變化, 修改MySet.INC,<br>然後Project/Buile All即可, 實在是夠簡單的了。<br>基本動作會了之後, 讓我告訴你多一點有關{$I檔案名稱}的事。<br>Ø 一旦應用了{$I檔案名稱}, 幾乎等於Compiler在編譯時, 讓Compiler將這個檔<br>案的內容貼進我們的程式中的那個位置。<br>Ø 如果沒有註明副檔名, Delphi預設這個檔案是.PAS。<br>Ø 如果在專案的目錄中找不到這個檔案的話, Delphi會陸續搜尋Tools/Options<br>/Library中的Library Path中的目錄。<br>另外, 當您寫作了一個DLL, 使用者在使用其中的函數前必須宣告過, 如果能夠一併提<br>供這些函數的宣告檔, 使用者只要一行{$I xxx}<br>即可, 是不是很方便呢?<br>{$I+}&nbsp; EInOutError檢查<br>在{$I+}(系統預設值)狀態編譯的程式, 一旦發生I/O錯誤時, 將會舉發一個EInOutError<br>的例外, 假如我們在特定的情況下不希望出現這個例外的訊息時(例如前文提到的偵測檔<br>案是否存在函數), 可以將這個Compiler directive設為{$I-}, 此時, 程式執行時是否發<br>生過錯誤,程式設定師必須自行檢查IOResult這個公用變數的值, 如果是零, 表示沒有錯誤,<br>非零的錯誤代碼含意請詳查Online help。<br>{$L檔案名稱} 連結目標檔<br>如果您有一個.OBJ檔要併入Delphi的程式時, 可以在程式中加入:<br>{$L OTHER.OBJ}<br>這樣, 就可以使用OTHER.OBJ中的程式了, 值得注意的是, 函數或程序在呼叫前,<br>仍然必須用external宣告過, 表明這些模組是來自「外部」的函式。<br>舉例來說, 筆者有一份由Keypro廠商提供的.OBJ檔, 在使用時, 相關的程式如下:<br>…<br>{$L hasptpw.obj}<br>{$F+}<br>procedure hasp (Service, SeedCode, LptNum, Pass1, Pass2 : word;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var p1,p2,p3,p4 : word); external;<br>{$F-}<br>…<br>經過{$L hasptpw.obj}宣告之後, 程式的其他部分就可以直接呼叫原先位於 hasptpw.obj<br>中的hsap這個程序了。<br>{$L+} 區域符號資訊<br>在{$L+}時, Delphi會額外加入一些區域符號資訊, 這使得我們可以應用Delphi IDE中的<br>View/Call Stack, View/Watch在程式執行時檢視變數內容與函式呼叫的關係。<br>應用這個Compiler directive的注意事項有:<br>Ø {$D-}時, {$L+}不會有作用。<br>Ø 使用{$L+}, 只會加大.DCU的檔案大小, 對.EXE的大小與執行速度並沒有影響。<br>{$H+} 長字串宣告<br>Delphi 2.0之後, 字串多了一個更為好用的長字串, 不僅沒有資料長度255的限制,<br>與C語言慣用的Null-terminated string相容性也大為提高。<br>使用{$H}時的注意事項有:<br>Ø {$H+}的編譯情形下, 以string定義的字串變數都是長字串, 請注意,<br>字串是否為長字串是在字串定義時決定的, 例如:<br>procedure TForm1.Button1Click(Sender: TObject);<br>{$H-}<br>var<br>&nbsp; s: string;<br>begin<br>{$H+}<br>&nbsp; s := '測試一下長字串';<br>&nbsp; Windows.MessageBox(0, pchar(s), '訊息', 64);<br>end;<br>由於var前{$H-}的緣故, 雖然在begin後我們立即設定為{$H+}, 但s仍然是一個短字串,<br>所以, 自然不能像是長字串一樣, 以pchar強制型別轉換後當作Null-terminated字串使<br>用。<br>Ø 承上, 不管程式是{$H+}或{$H-}, 只要字串是以長字串方式定義的, 即使<br>begin..end;中改成{$H-}, 該字串的操作仍然具有長字串的特性。<br>因此, 由於VCL中的字串都是長字串, 即使我們的程式是{$H-}, 仍然可以拿它們當長<br>字串來使用。<br>Ø 不論{$H}的狀態如何, 以AnsiString定義的一定是長字串; 以string或<br>ShortString定義的一定是短字串。<br>{$M 16386, 1048576} 記憶體配置大小<br>要改變唯疊(Stack)記憶體配置大小時, 我們可以有以下兩種選擇:<br>Ø 使用{$MINSTACKSIZE數字}, {$MAXSTACKSIZE數字}, 分別指定最小.最大的Stack<br>大小.<br>Ø 或者使用{$M min, max}, 同時指定最小與最大的值。<br>使用這些Compiler directive時的注意事項有:<br>Ø 寫在.DPR中才有效果。<br>Ø 堆疊的最小數字必須介於1024至21474835647之間。<br>Ø 堆疊的最大數字必須介於$MINSTACKSIZE至21474835647之間。<br>Ø 當記憶體不足而無法滿足最小的堆疊大小時, Windows會在啟動這程式時<br>提出錯誤報告。<br>Ø 當程式要求的記憶體超過$MINSTACKSIZE的大小時, 將舉發EStackOverflow例外。<br>{$Z1} 最小列舉大小<br>這個Compiler directive將影響儲存列舉型態時最小所需的byte數值。如果宣告列舉型<br>態時, 數值不大於256, 而且也在系統預設的{$Z1}時, 這個列舉型態只佔用一個byte儲<br>存的。{$Z2}時, 以兩個byte儲存, {$Z4}時, 以四個byte儲存。因為C語言通常以WORD或<br>DWORD儲存列舉型態, 如果您的程式需要與C、C++溝通時,{$Z2}{$Z4}就很管用了<br>{$Z+}, 與{$Z-}分別對應到{$Z1}和{$Z4}。<br>{$P+} 開放字串參數<br>在程序與函數宣告時, 其中的字串引數, 在{$P+}時表示是Open string; {$P-}時,<br>只是一般的字串變數而已。這個Compiler directive只在{$H-}時有作用。<br>{$O+} 最佳化開關<br>建議您維持{$O+}的系統預設值。開啟這個Compiler directive, Delphi會自動進行<br>最佳化處理, 程式可以因此跑得快一些, 您可以放心的打開這個編譯開關, Delphi<br>不會進行不安全的最佳化而使您的程式執行時發生錯誤。<br>{$Q-} 滿溢檢查, {$R-} 範圍檢查<br>{$Q}與{$R}是一組搭配使用的Compiler directive, 它們將檢查數值或陣列的<br>操作是否在安全的邊界中, {$Q}會檢查整數運算(如+, -, Abs, Sqr, Pred,<br>Succ等), 而{$R}則檢查字串與陣列的存取是否超出合理邊界範圍等問題。<br>使用這兩個Compiler directives會因為這些檢查動作而降低程式執行的速度,<br>通常我們會在除錯時開啟這兩個編譯開關。<br>{$U-} Pentium CPU浮點運算安全檢查<br>還記得早期Pentium CPU浮點運算不正確的事吧? 這批CPU應該回收得差不多了,<br>但如果您仍然不確定程式會不會意外的遇到漏網之魚或黑心牌經銷商的話, 請將這個<br>Compiler directives設為{$U+}。<br>根據Borland手冊的說明, 如果CPU是沒有暇疵的, 設定{$U+}對於執行速度只有輕微<br>的影響; 但如果是問題CPU, 浮點的除法速度會因此慢上三倍, 是否要打開這個開關,<br>您心中應該已有取捨。<br>{$R檔案名稱} 資源檔<br>在您還沒有開始學習Compiler directives之前, 這個指令就已經出現在您的程式中<br>了,每次開出一個新的form時, Delphi自動在Implement開頭部分中加入{$R *.DFM},<br>在Project/Source中看到的.DPR程式中也有{$R *.RES}, 這些是什麼意思呢? 意思是說,<br>在編譯連結時, 含入與專案主檔名同名的.RES, 以及與form unit檔案同名的.DFM等資源檔。<br>如果您需要在程式中使用額外的資源(例如: 自訂滑鼠指標), 請注意不要自行以Resouse<br>WorkShop或Image Editor等資源編輯器更改這些與Project或Form同名的資源檔,<br>改變這些同名的檔案不僅無效, 可能還有不可預期的錯誤。因些,<br>您應該在另外一個資源檔中存放這些資源, 並於{$R}中寫明檔案的名稱將其連結進來, 例如:<br>{$R MyCursor.res}<br>{$T-} @指標型態檢查<br>應用@運算元可以取得變數的位址, 在{$T-}時, 以@取得是一個無型別的指標(Pointer)。<br>反過來說, 在{$T+}時, 是有型別的指標, 假定I是一個integer的變數, @I所得到的即是<br>相當於^Integer(Pointer of Integer)的指標。<br>{$WARNINGS ON} 編譯器警告<br>這個Compiler directive與{$HINTS}的作用類似, 同樣會對程式的可能問題提出警告。<br>不同的是, 在{$WARNINGS ON}時, Compiler會對未初始化的變數、沒有傳回值的函數、<br>建構抽象物件等情況提出警告。<br>{$J-} 型態常數唯讀<br>從前筆者曾經對以下的程式產生過疑惑:<br>{$J+}<br>procedure TForm1.Button1Click(Sender: TObject);<br>const<br>&nbsp; VarConst: integer = 4;<br>begin<br>&nbsp; VarConst := 5;<br>&nbsp; ShowMessage(IntToStr(VarConst));<br>end;<br>const不是常數嗎? 為什麼可以改呢? 在先前的Pascal版本中, 以<br>const VarName: DataType = const value;<br>定義的具型態常數的確是可以改的, 假如您希望常數就是常數, 它不應該允許修改,<br>請將這個Compiler directive設為{$J-}<br>不論是{$J+}或{$J-}, 以const VarName = const value; 定義的常數(<br>沒有加上型別宣告), 是一個真正的常數, 其他的程式不可以改變其內容。<br>其實{$J+}時還有一個妙用, 那就是宣告出類似C語言static的變數, 換句話說,<br>產生了一個與Application相同生命週期的變數。在這種情形下, 變數只在第一<br>次使用時才會建立, 函數或程序結束時, 該變數也不會消滅, 下一次再呼叫到這個函數<br>或程序時, 我們仍然可以參考到上次執行結束時的值。讓我們試一下這個例子:<br>{$J+}<br>procedure TForm1.Button1Click(Sender: TObject);<br>const<br>&nbsp; i: integer = 0;<br>begin<br>&nbsp; ShowMessage(IntToStr(i));<br>&nbsp; Inc(i);<br>&nbsp; ShowMessage(IntToStr(i));<br>end;<br>第一次執行時, 我們分別會看到「0」「1」, 再點一次這個按鈕時, 看到的將是「1」「2」。</p>
<p>&nbsp;</p>
<p>在工程文件的最前面添加上<br>{$APPTYPE CONSOLE}</p>
<p>在代码中加上如下调试代码:<br>{$IFDEF CONSOLE}<br>&nbsp; Writeln('=====');<br>{$ENDIF}<br>将会在控制台中输出信息。<br><br></p><br><br>
来源:https://www.cnblogs.com/maweiwei/p/12807256.html
頁: [1]
查看完整版本: Delphi编译开关(转帖收藏)