注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

yeye55的博客

编程就象品一杯清茶,清淡却又深厚。

 
 
 

日志

 
 
关于我

宅男一只,喜欢编程。没事的时候就喜欢抱着电脑看看电影、看看动漫、听听音乐、打打网游、逛逛论坛。清静的时候喜欢专研编程技术,但是发现自己至今仍然只是一只三脚猫。

网易考拉推荐

Delphi XE4 语言指南 - 3 语法元素  

2014-11-25 19:17:13|  分类: XE4参考指南 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

  本指南名称:Delphi XE4 语言指南,作者:叶叶,于2014年11月25日在网易博客上发表。页面地址:http://yeye55blog.blog.163.com/blog/static/197241021201410252936156/ 。本指南全文可以在上述页面中下载。请尊重他人劳动成果,转载或引用时请注明出处。

3 语法元素

  Delphi语言的字符集使用Unicode字符编码,包含了字母和数字的Unicode字符以及下划线。Delphi是不区分大小写的。空格字符和控制字符(U+0000至U+001F,包括U+000D回车符和行终止字符)被视为空白字符(blank)。编译器支持带字节顺序标记的UCS-2和UCS-4编码的文件。但是使用UTF-8之外的其他格式文件会使编译速度受到影响。在UCS-4编码的源文件中的所有字符必须在UCS-2没有代理对(surrogate pair)的情况下可以被表示。UCS-2编码以及代理对(包含GB18030)必须在指定了编译器的代码页选项之后才可以被接受。

  构成Delphi语法的最基本的语法元素称为标记(token),多个标记结合起来可以形成表达式(expression)、声明(declaration)和语句(statement)。语句是用于在程序中描述一个可以被执行的算法。表达式是出现在语句中并代表一个值的语法单元。声明用于定义一个可以在语句和表达式中使用的标识符(如变量或函数的名称),并在适当的情况下,为标识符分配内存。声明和语句被组织到块(block)中,多个块构成了程序。可以说,程序完全是由组织到块中的声明和语句所构成的。关于声明的详细介绍请参考第3.9节。关于块的详细介绍请参考第3.10节。关于表达式的详细介绍请参考第6节。关于语句的详细介绍请参考第7节。

  从最简单的层面上来说,程序是由分隔符(separator)分隔的多个标记所组成的一种序列。标记是程序文本中最小的有意义的单位。分隔符可以是一个空白字符或注释(comment),并且在部分情况下可以被省略。标记分为特殊符号(special symbol)、保留字(reserved word)、指令(directive)、标识符(identifier)、字面量(literal)和标签(label)。字面量分为数字(numeral)和字符串(character string)。当标记是一个字符串字面量时,分隔符可以成为标记的一部分。当标记是一个保留字、标识符、数字字面量或标签时,两个标记之间必须要有一到多个分隔符进行分隔。

3.1 特殊符号

  特殊符号(special symbol)是由非字母数字的字符所组成,带有特定的含义。特殊符号可以由单个字符组成,也可以由两个字符组成。Delphi中所使用的特殊符号如下所示。

由单个字符组成的特殊符号:
# $ & ' ( ) * + , - . / : ; = @ [ ] ^ { }
由两个字符组成的特殊符号:
(* (. *) .) .. // := <= >= <> 

  在Delphi中有几个特殊符号具有相同的含义,如下所示。

特殊符号: [ ] { }
相同含义的特殊符号: (. .) (* *)

  注意,下面列出的这些标点符号不是Delphi中的特殊符号。

% ? \ ! " _ | ~

3.2 保留字

  保留字(reserved word)就是被编译器所保留用于特殊用途的英文单词。保留字不能被重新定义,也不能用作标识符。Delphi中的保留字如下所示。

and div function library raise try
array do goto mod record type
as downto if nil repeat unit
asm else implementation not resourcestring until
begin end in object set uses
case except inherited of shl var
class exports initialization or shr while
const file inline packed string with
constructor finalization interface procedure then xor
destructor finally is program threadvar
dispinterface for label property to

  注意,除了上面列出的这些单词,private、protected、public、published和automated在类类型声明中也被视为保留字,但是在其他地方被视为指令。单词at和on具有特殊的含义,应当被视为保留字。关键字“of object”用于定义方法指针。

3.3 指令

  指令(directive)就是在源代码的特定位置有着特殊意义的英文单词。在Delphi语言中,指令不同于保留字。保留字不能用作用户定义的标识符,而指令只对源代码中的特定位置敏感,所以可以将指令用作标识符(但是不推荐这么做)。可以将指令放置在标识符声明的末尾,从而修改该声明的含义。Delphi中的指令如下所示。

absolute dynamic inline override reference stored
abstract experimental library package register unsafe
assembler export local pascal reintroduce varargs
automated external message platform requires virtual
cdecl far name private resident winapi
contains final near protected safecall write
default forward nodefault public sealed writeonly
delayed helper operator published static
deprecated implements out read stdcall
dispid index overload readonly strict

  Delphi提供了多种类型的指令,除了上面介绍的对特定位置敏感的指令外,还有一种指令是编译指令,关于编译指令的详细介绍请参考第3.8节。另外,在声明中可以使用提示指令来产生警告消息,关于提示指令的详细介绍请参考第3.9.1节。

3.4 标识符

  标识符(identifier)用来表示常量、变量、字段、类型、属性、过程、函数、程序、单元、库和包。标识符可以是任意长度,但是只有最前面的255个字符是有效的。标识符必须以字母、Unicode字符或下划线“_”开始,并且不能包含空格。在第一个字符之后可以出现字母、数字、Unicode字符和下划线。汉字可以用作标识符,将会被视为Unicode字符。保留字不能用作标识符。由于Delphi语言是不区分大小写的,所以如果两个标识符仅有大小写上的区别,会被编译器认为是完全相同的。

  将两个以上的标识符使用点号“.”连接起来就形成了限定标识符(qualified identifier)。例如对于identifier1.identifier2,我们说标识符identifier1是标识符identifier2的限定词。在访问记录或类的成员时,或者访问两个单元中重名的标识符时,都需要使用限定标识符。如果你不使用限定标识符,那么对于标识符的访问将由块的可视作用域以及声明和语句的作用域中的相关规则来确定。一些使用限定标识符的例子如下所示。

01 Unit2.CurrentValue := 0;
02 Rect.Height := 50;
03 Rect.Width := 50;
04 Form1.Caption := 'abc';
05 Form1.Button1.Click;
06 Unit1.Form1.Edit1.Text := 'ABC';

代码3.1 限定标识符的使用

  在Delphi语言中,保留字不能用作标识符。但是,如果你必须要声明一个与保留字相同名称的标识符的话,那么你可以在标识符前加上“&”符号。这样的标识符被称为扩展标识符(extended identifier)。使用扩展标识符可以防止一个标识符被当成保留字解析。注意一点,如果要在限定标识符中使用扩展标识符,那么每个扩展标识符前都要加上“&”符号。一个使用扩展标识符的例子如下所示。

01 type
02     &type = record
03         &begin : Integer;
04         &end : Integer;
05     end;
06
07 procedure &procedure;
08 var
09     &try : &type;
10     Data : &type;
11 begin
12     &try.&begin := 100;
13     &try.&end := 100;
14     Data.&begin := 50;
15     Data.&end := 50;
16 end;

代码3.2 扩展标识符的使用

3.5 字面量

  字面量(literal)就是在程序源代码中直接由文本给出的量。代码可以直接使用这些量,不需要通过标识符来访问。字面量通常用来初始化常量或变量,也可以放置在表达式中一起参与运算。在Delphi语言中支持两种类型的字面量:数字和字符串。

3.5.1 数字

  数字(numeral)用来表示一个整数或实数。数字可以使用一个没有逗号和空格的采用十进制记数法的数字序列来表示。数字之前可以使用加号“+”或减号“-”来指定符号。数字在默认情况下为正数,即,67258相当于+67258。数字所表示的数值必须在整数或实数类型的预定义最大范围内。

  在数字中可以使用小数点和指数来表示实数,其他数字则用来表示整数。在数字中使用字符“E”或“e”来表示实数,其含义是10的次方数。例如,7E2表示7×102,12.25e+6和12.25e6都表示12.25×106

  要表示十六进制的数字,需要在数字之前使用美元符号“$”。例如:$8F。十六进制数字之前不能使用减号“-”,默认都取正值。在赋值时,如果一个十六进制值超出了接受类型的范围,那么除了整数之外的其他类型都将引发一个错误,而整数类型(32位整形)只会引发一个警告。在这种情况下,超出整数类型取值范围的十六进制数字都会被忽略。即,对于32位整形来说,只有末尾的8位十六进制数字才会被采纳。

  一些使用数字字面量的例子如下所示。

01 var
02     i1, i2, i3 : Integer;
03     r1, r2, r3 : Real;
04 begin
05     i1 := 67258;              //67258
06     i2 := $8F;                   //143
07     i3 := -109;                 //-109
08     r1 := 7E2;                  //700
09     r2 := 12.25e6;          //12250000
10     r3 := 123.456;          //123.456
11 end;

代码3.3 数字字面量的使用

3.5.2 字符串

  字符串(character string)也被称为字符串字面量或字符串常量,包括了带引号的字符串、控制字符串、以及带引号字符串和控制字符串的串联。分隔符只能出现在带引号字符串内。

  带引号字符串是一个字符序列,可以由ANSI或多字节字符集中的字符所组成,必须写在同一行,并且使用单引号“'”括起来。在带引号字符串的两个单引号之间一个字符都没有的称为空字符串。在带引号字符串之内,使用两个连续的单引号来表示一个单引号字符。

  控制字符串是由一到多个控制字符所组成,每个控制字符由一个井号“#”跟一个无符号整数所构成。无符号整数用来表示对应字符的UTF-16编码的编码值,其取值范围是0至65535(十进制)或$0至$FFFF(十六进制)。字符串中的每个字符使用2个字节来表示。需要在字符串中加入控制符号(如换行符)时,控制字符就显得非常有用。

  可以将带引号字符串与控制字符串串联起来形成更大的字符串。不过要注意,两个带引号字符串不能进行串联。因为两个连续的单引号会被解释为一个单引号字符。串联两个带引号字符串时需要使用“+”运算符。

  字符串在内部使用UTF-16编码的Unicode字符串来表示。对于基本多文种平面(Basic Multilingual Plane,BMP)中定义的字符(包括拉丁字母、汉字和其他文字或符号),使用2个字节来保存。对于辅助平面中定义的字符,以代理对(surrogate pair)的方式使用4个字节来保存。

  字符串字面量可以与任何一种字符串类型以及PChar类型相兼容。字符串字面量中的字符可以与任何一种字符类型相兼容。由于AnsiString类型的字符串可以包含多字节字符,一个字符串字面量中的字符在AnsiString类型字符串中可能占用一个或多个字节。当启用了扩展语法(编译指令{$X+})后,一个长度为n的非空字符串字面量可以与一个具有n个元素的一维静态字符数组相兼容。

  一些使用字符串字面量的例子如下所示。

01 var
02     s1, s2, s3, s4, s5, s6, s7 : String;
03     a1, a2 : array [1..4] of Char;
04 begin
05     s1 := 'You''ll see';                                //You'll see
06     s2 := '大家好,我是字符串';         //汉字字符串
07     s3 := '''';                                                 //一个单引号
08     s4 := '';                                                   //空字符串
09     s5 := ' ';                                                  //一个空格
10     s6 := #89#111#117;                           //You
11     s7 := '第一行'#$D#$A'第二行';      //两行字符串
12     a1 := 'abcd';                                         //字符串兼容数组
13     a2 := '甲乙丙丁';                                //也可用于汉字
14 end;

代码3.4 字符串字面量的使用

3.6 标签

  你可以使用一个标识符或者是一个非负整数来定义一个标签(label)。使用非负整数来定义标签时,整数的取值范围是0至4294967295(32位无符号整形)。标签是和goto语句一起使用的。关于标签的声明和goto语句的详细介绍请参考第7.3节。

3.7 注释

  注释(comment)通常用来对源代码进行说明,编译器会忽略注释的内容。注释可以用作分隔符和编译指令。Delphi支持三种方式的注释,一种单行注释和两种多行注释。

  单行注释使用两个斜线“//”来进行,从两个斜线开始直到行末的文本都将成为注释。在IDE的代码编辑器中可以使用快捷键Ctrl+/将输入光标所在的行注释掉。

  多行注释使用一对大括号“{}”来进行,大括号之内的文本都将成为注释。这些文本允许是多行的。一对括号星“(**)”也具有相同的功能,也可以进行多行注释。用于多行注释的大括号或括号星必须成对出现,并且不能出现交叉重叠的情况。如果要对注释进行嵌套,必须采用不同的方式进行注释,例如(*{}*)。多行注释可以嵌入到代码之间,不会影响代码的运行功能。在多行注释中的第一个字符如果是美元符号“$”,那么这个多行注释将被视为一个编译指令。关于编译指令的详细介绍请参考第3.8节。

  在编写代码时应该对重要的标识符或语句进行注释,以说明其相关的功能和作用。这样可以提高代码的可读性,使得代码维护人员更容易理解代码的含义。在调试程序时可以将一些代码注释掉,然后观察程序运行状态的改变,借以找出错误的代码。一些使用注释的例子如下所示。

01 //大家好,我是单行注释
02
03 {大家好,我是多行注释
04     这里是第二行}
05
06 (*大家好,我还是多行注释
07     这里还是第二行*)
08
09 {大家好,(*我是嵌套注释*)}

代码3.5 注释的使用

3.8 编译指令

  使用编译指令(compiler directive)可以修改编译器的参数以及编译时的行为。在一个源代码中,编译指令以注释的方式存在。这个注释使用了特殊的语法,在注释中的第一个字符必须是美元符号“$”,其后紧跟编译指令的名称(一到多个字母),然后是编译指令所需的参数,在最后还可以加入其他注释。编译指令可以放置在任意的一种多行注释中(如“{}”或“(**)”),但是不能放置在单行注释中。编译指令分为三种:开关指令(switch directive)、参数指令(parameter directive)和条件编译指令(conditional compilation directive)。注意,除开关指令之外,其他所有指令的指令名称与参数之间必须使用至少一个空格将其隔开。一些使用编译指令的例子如下所示。

01 {$B+}
02 {$R- Turn off range checking}
03 {$I TYPES.INC}
04 {$M 32768,40960}
05 {$DEFINE Debug}
06 {$IFDEF Debug}
07 {$ENDIF}

代码3.6 编译指令的使用

  除了在源代码中直接插入编译指令的这种方式外,还有两种方式来使用编译指令。一种是在命令行方式中使用编译指令,另一种是在IDE中指定编译指令。注意一点,插入在源代码中的编译指令优先于另外的两种方式。

  在操作系统的命令提示符窗口中,以命令行的方式来调用编译器时,可以使用命令行参数“-$指令”来指定编译指令。例如,使用编译器dcc32来编译一个名为Greeting.dpr的项目文件时,希望包含资源文件Greeting.res,那么可以使用下面的命令。

dcc32 Greeting -$R Greeting

  在IDE中可以使用项目选项对话框(Project > Options > Delphi Compiler > Compiling)来修改编译指令。在这个对话框中所做的修改将会影响到所有的单元文件,当对应的项目文件被编译时单元文件的源代码将会被重新编译。如果修改了编译器的参数设置,那么所有单元文件中的源代码将会使用新的设置进行重新编译。在IDE的代码编辑器中编写一个源文件时,如果希望快速查看这些编译设置的效果,可以使用快捷键Ctrl+O+O。这个快捷键会把当前的编译设置以编译指令的方式添加到当前文件的开头。如果希望删除这些添加的内容,可以使用撤消键Ctrl+Z。

  关于Delphi中的编译指令的列表请参考《Delphi compiler directives list[2]》。

3.8.1 开关指令

  开关指令(switch directive)用于启用或禁用编译器的特定功能。开关指令有简写和长名两种方式。使用简写方式时,只需要给出一个字母作为其指令名称,然后紧跟一个加号“+”或减号“-”用来表示启用和禁用。例如:{$A+}。使用长名方式时,必须要给出完整的指令名称,然后使用单词on或off来表示启用和禁用。这两个单词与指令名称之间必须使用至少一个空格将其隔开。例如:{$ALIGN ON}。

  多个使用简写方式的开关指令可以编写在同一个编译指令中,每个指令之间只能使用逗号“,”进行分隔,不能插入空格。例如:{$B+,R-,S-}。使用长名方式的开关指令不支持这种编写方式。例如:{$OPTIMIZATION ON,HINTS OFF},在逗号之后的HINTS OFF部分将会被编译器视为注释从而被忽略掉。长名方式的开关指令必须单独使用。另外,不是所有的开关指令都支持简写方式,关于这一点请参考Delphi自带的帮助文件。

  从开关指令用作范围的角度来说,开关指令分为全局指令和局部指令。全局指令将影响到整个编译过程,必须放置在被编译项目文件或单元文件的声明部分的前面。局部指令的影响范围是从指令出现开始,直到相同指令再次出现之前。局部指令可以出现在任何地方。

  关于Delphi支持的开关指令的介绍请参考《Delphi Compiler Directives[2]》。

3.8.2 参数指令

  参数指令(parameter directive)可以指定一个影响编译效果的参数。最常见的就是用于包含资源文件的参数指令,例如:{$R *.res}。与开关指令一样,参数指令也有简写和长名两种方式,参数指令也分为全局指令和局部指令。关于这些内容请参考第3.8.1节的开关指令。

  关于Delphi支持的参数指令的介绍请参考《Delphi Compiler Directives[2]》。

3.8.3 条件编译指令

  条件编译指令(conditional compilation directive)可以让编译器根据指定的条件只编译部分代码或者是忽略部分代码。指定的条件可以是某个条件符号(conditional symbol)是否被定义,或者是开关指令的当前状态,或者是一个布尔类型的常量表达式。Delphi中的条件编译指令一共有十个:{$DEFINE}、{$UNDEF}、{$IFDEF}、{$IFNDEF}、{$IFOPT}、{$IF}、{$ELSEIF}、{$ELSE}、{$ENDIF}和{$IFEND}。以下将{$IFDEF}、{$IFNDEF}、{$IFOPT}和{$IF}统称为{$IFxxx}指令。

  {$DEFINE}指令的样式是{$DEFINE 名称},{$DEFINE}指令用于定义一个指定名称的条件符号。条件符号的名称必须由字母开头,其后跟字母、数字、下划线的任意组合。条件符号的名称可以是任意长度,但是只有最前面的255个字符是有效的。条件符号不是Delphi的标识符,不能在实际的程序代码中引用。条件符号相当于布尔类型的变量,有两种状态:已定义(True)和未定义(False)。任何有效的条件符号都被初始化为未定义,直到使用{$DEFINE}指令对其进行定义。使用{$DEFINE}指令进行定义后,对应的条件符号将被视为已定义,直到使用{$UNDEF}指令对其进行取消定义。如果对应的条件符号已经被定义了,那么使用{$DEFINE}指令将没有任何影响。另外,使用命令行方式调用编译器时,可以使用“-D”参数来定义一个条件符号。在IDE中也可以使用项目选项对话框(Project > Options > Delphi Compiler > Conditional Defines)来定义一个条件符号。

  {$UNDEF}指令的样式是{$UNDEF 名称},{$UNDEF}指令用于将一个指定名称的条件符号取消定义。取消定义后,对应的条件符号将被视为未定义,直到使用{$DEFINE}指令对其进行重新定义。如果对应的条件符号还未被定义,那么使用{$UNDEF}指令将没有任何影响。注意,通过命令行参数或项目选项对话框定义的条件符号都将在编译每一个单元文件之前被恢复。在单元文件中定义的条件符号都将在编译下一个单元文件之前被丢弃。

  {$IFDEF}指令的样式是{$IFDEF 名称},{$IFDEF}指令用于发起一个条件编译。如果指定名称的条件符号是已定义的,那么{$IFDEF}指令之后的代码就会被编译器编译。反之,如果指定名称的条件符号是未定义的,那么{$IFDEF}指令之后的代码就会被编译器忽略。在{$IFDEF}指令中引用的条件符号必须是由{$DEFINE}指令定义的。在{$IFDEF}指令中不能引用Delphi中的标识符。{$IFDEF}指令可以和{$ELSE}指令进行联用,联用{$ELSE}指令后可以实现双分支。在双分支中,只有一个分支中的代码会被编译,而另一个分支中的代码会被忽略。另外,利用“if ... else if ... else”格式可以实现多重条件分支。在多重条件分支中,只有一个分支中的代码会被编译,其他分支中的代码都会被忽略。一个使用{$IFDEF}指令的例子如下所示。

01 {$DEFINE MyDef2}
02
03 const
04 {$IFDEF MyDef1}
05     N = 1;
06 {$ELSE}{$IFDEF MyDef2}
07     N = 2;
08 {$ELSE}{$IFDEF MyDef3}
09     N = 3;
10 {$ELSE}
11     N = 0;
12 {$ENDIF}
13 {$ENDIF}
14 {$ENDIF}
15
16 var
17     i : Integer;
18 begin
19     i := N; //i等于2
20 end;

代码3.7 {$IFDEF}指令的使用

  {$IFNDEF}指令的样式是{$IFNDEF 名称}。{$IFNDEF}指令的功能与{$IFDEF}指令完全相同,但是作用相反。在{$IFNDEF}指令中,如果指定名称的条件符号是未定义的,那么{$IFNDEF}指令之后的代码就会被编译器编译。反之,如果指定名称的条件符号是已定义的,那么{$IFNDEF}指令之后的代码就会被编译器忽略。

  {$IFOPT}指令的样式是{$IFOPT 开关指令},{$IFOPT}指令也是用于发起一个条件编译。在{$IFOPT}指令中指定的开关指令,如果与当前对应的开关指令的状态相一致,那么{$IFOPT}指令之后的代码就会被编译器编译。反之,如果状态不一致,那么{$IFOPT}指令之后的代码就会被编译器忽略。在{$IFOPT}指令中的开关指令必须使用简写方式,即,只能给出一个字母作为其指令名称,然后紧跟一个加号“+”或减号“-”用来表示启用状态和禁用状态。由于{$IFOPT}指令只支持简写方式,那些不支持简写方式的开关指令将无法应用于{$IFOPT}指令。一个使用{$IFOPT}指令的例子如下所示。

01 const
02 {$IFOPT B-}
03     N = 1;
04 {$ELSE}
05     N = 0;
06 {$ENDIF}
07
08 var
09     i : Integer;
10 begin
11     i := N;
12     //如果当前状态是{$B-},那么i等于1
13     //如果当前状态是{$B+},那么i等于0
14 end;

代码3.8 {$IFOPT}指令的使用

  {$IF}指令的样式是{$IF 表达式},{$IF}指令同样用于发起一个条件编译。{$IF}指令中的表达式必须是一个布尔类型的常量表达式,必须符合Delphi的语法。如果该表达式返回True,那么{$IF}指令之后的代码就会被编译器编译。反之,如果该表达式返回False,那么{$IF}指令之后的代码就会被编译器忽略。在{$IF}指令的表达式中引用的标识符如果是不存在的,那么该表达式将被视为False。{$IF}指令支持对类型常量进行评估,但是不允许类型常量出现在常量表达式的运算中。一些使用{$IF}指令的例子如下所示。

01 const
02 {$IF NoExist}    //标识符NoExist不存在
03     N1 = 1;    //该行代码被忽略
04 {$ELSE}
05     N1 = 0;    //该行代码被编译
06 {$IFEND}
07
08     V1 : Integer = 10;   //类型常量
09     V2 = 5;                       //真实常量
10
11 {$IF SizeOf(V1) = 4}           //正确
12     N2 = 1;
13 {$ELSE}
14     N2 = 0;
15 {$IFEND}
16
17 {$IF V1 > 0}                          //错误
18     N3 = 1;
19 {$ELSE}
20     N3 = 0;
21 {$IFEND}
22
23 {$IF V2 > 0}                          //正确
24     N4 = 1;
25 {$ELSE}
26     N4 = 0;
27 {$IFEND}

代码3.9 {$IF}指令的使用

  {$ELSEIF}指令的样式是{$ELSEIF 表达式},{$ELSEIF}指令相当于是{$ELSE}指令与{$IF}指令的结合。使用{$ELSEIF}指令也可以实现多重条件分支。一个使用{$ELSEIF}指令的例子如下所示。

01 const
02 {$DEFINE MyDef}
03
04     V1 : Integer = 10;   //类型常量
05     V2 = 5;                       //真实常量
06
07 {$IF Defined(MyDef) and (V2 > 0)}
08     N1 = 1;    //该行代码被编译
09 {$ELSEIF V2 = 0}
10     N1 = 2;    //该行代码被忽略
11 {$ELSE}
12     N1 = 0;    //该行代码被忽略
13 {$IFEND}
14
15 {$IF Declared(V1)}
16     N2 = 1;    //该行代码被编译
17 {$ELSEIF V2 > 0}
18     N2 = 2;    //该行代码被忽略
19 {$ELSEIF V2 < 0}
20     N2 = 3;    //该行代码被忽略
21 {$ELSE}
22     N2 = 0;    //该行代码被忽略
23 {$IFEND}

代码3.10 {$ELSEIF}指令的使用

  在代码3.10中,使用了两个特殊的函数Defined和Declared。这两个函数都返回布尔类型的值,并且只能在{$IF}和{$ELSEIF}指令中使用。Defined函数用于测试某个条件符号是否被定义。如果指定的条件符号是已定义的,则返回True。反之,如果是未定义的,则返回False。Declared函数用于测试某个Delphi标识符是否被声明。在当前的范围内,如果指定的标识符是可见的,则返回True。反之,如果是不可见的,则返回False。

  {$ELSE}和{$ELSEIF}指令不能用于发起一个条件编译,但是它们可以和任意一个{$IFxxx}指令进行联用。在一个条件编译结构中,可以联用多个{$ELSEIF}指令,但只能联用一个{$ELSE}指令。在同时联用{$ELSE}和{$ELSEIF}指令时,{$ELSE}指令只能出现在其他{$ELSEIF}指令的后面。{$IFxxx}指令可以嵌套使用,最高嵌套层次可以达到32层。所有由{$IFxxx}指令发起的条件编译,都必须使用{$ENDIF}或{$IFEND}指令进行终止。由{$IF}指令发起的条件编译,必须使用{$IFEND}指令进行终止。对于由{$IFDEF}、{$IFNDEF}和{$IFOPT}指令发起的条件编译:如果联用了{$ELSEIF}指令,那么必须使用{$IFEND}指令进行终止;如果没有联用{$ELSEIF}指令,那么必须使用{$ENDIF}指令进行终止。对于每个{$IFxxx}指令,与之对应的{$ENDIF}或{$IFEND}指令必须在同一个源文件中。

  在Delphi中预定义了一些条件符号和常量。例如,条件符号VER250、WIN32、WIN64、CPUX86、CPUX64;以及常量CompilerVersion、RTLVersion、FireMonkeyVersion、等等。这些条件符号和常量反映了编译器版本、针对平台、针对CPU、运行时库版本、FireMonkey版本等各种相关的信息。在条件编译指令中可以引用这些条件符号和常量,利用这些信息可以针对不同的运行环境编写不同的代码。标准条件符号的定义请参考《Predefined Conditionals[3]》。编译器版本号列表请参考《Compiler Versions[4]》。其他的一些条件符号和常量请参考Delphi自带的帮助文件。

  在使用条件编译指令时需要注意一点。只有在重新编译源代码时,才会对条件定义进行评估。当你重建一个项目并修改了条件符号的状态后,那些未修改的单元文件的源代码可能不会被重新编译。这时需要重新构建项目(Project > Build All Projects),这样才可以让项目中的所有代码都反映出当前的条件符号的设置状态。

3.9 声明

  常量、变量、字段、类型、属性、过程、函数、程序、单元、库和包的名称被称为标识符。标识符必须先声明(declaration)然后才可以使用。唯一例外的是几个预定义的类型、例程和常量,它们是由编译器自动解释的。例如,函数块中的Result变量与方法实现中的Self变量。几个声明的例子如下所示。

01 var Size : Extended;
02
03 function DoThis(X, Y : String): Integer;
04
05 var
06     Size : Extended;
07     Quantity : Integer;
08     Description : String;

代码3.11 声明的例子

  在适当的情况下,当声明一个标识符时会为它分配内存。例如,在代码3.11中的第1行声明了一个名为Size的变量,它用于保存Extended类型(实数)的值。在代码3.11中的第3行声明了一个名为DoThis的函数,它接受两个字符串类型的参数,并返回一个Integer类型的整数。每个声明都必须以分号“;”结尾。如果需要同时声明多个变量、常量、类型或标签,那么相应的保留字只需要出现一次。例如,代码3.11中的第5行到第8行声明了三个变量。

  声明的语法和位置取决于所声明的标识符的类型。一般情况下,声明可以放置在块的开头,以及单元的接口部分和实现部分(在uses子句之后)。声明的具体语法和相关的具体约定请参考相关内容的对应介绍。

3.9.1 提示指令

  提示指令(hinting directive)用于在编译时产生警告消息。提示指令有三个:platform、deprecated和library,可以添加到任何声明中。提示指令可以应用于类型和变量的声明,类、接口和结构的声明,类或记录中字段的声明,过程、函数和方法的声明,以及单元的声明。

  如果一个提示指令被放置到单元的声明中,那么这就意味着该提示指令适用于单元中的所有内容。例如,针对Windows 3.1平台的OleAuto.pas单元在当前的Windows平台上是完全过时的。任何引用该单元或使用该单元中符号的操作都将产生一个过时(deprecated)警告消息。

  提示指令platform放置在符号或单元中用来提示相关的内容在不同的平台(platform)上可能是不存在的或者是具有相当大的差异。提示指令library放置在符号或单元中用来提示相关的代码在不同的库架构(library architecture)中可能是不存在的或者是具有相当大的差异。

  提示指令platform和library并没有指定哪一个平台或库。如果你的目标是编写跨平台的代码,那么你没有必要知道符号是针对哪一个平台的。当一个符号被标记为针对特定的一部分平台时,它会提示你让你知道你的可移植代码可能会出现问题。

  在过程和函数的声明中,提示指令必须与声明的其他部分分开,并且和结尾的分号放置在一起。一些使用提示指令的例子如下所示。

01 procedure SomeOldRoutine; stdcall deprecated;
02
03 var
04     VersionNumber : Real library;
05
06 type
07     AppError = class(Exception)
08         //此处省略部分代码...
09     end platform;

代码3.12 提示指令的使用

  在编译指令{$HINTS ON}和{$WARNINGS ON}状态下编译源代码时,每一个使用了提示指令的标识符声明都会产生一个相应的提示或警告。使用platform来提示某项内容是针对特定操作环境(如Windows)的。使用deprecated来提示某项内容已经被废弃或者仅为提供向后兼容支持。使用library来提示某项内容依赖于特定的库或组件框架。

  Delphi的编译器也支持提示指令experimental。可以使用这个提示指令来提示某个单元正处于不稳定的开发状态。当正在构建的应用程序中引用了这样的单元时,编译器将会产生一个警告消息。

3.10 块

  声明和语句被组织到块(block)中。使用块可以让具有相同名称的标识符在不同的块中有不同的含意。块是声明程序、函数或过程中的一部分。每个程序、函数或过程的声明中都包含了一个块。块由一系列的声明其后紧跟一个复合语句所构成。所有的声明都必须放置在块的开头。块的语法格式如下所示。

{声明部分}
begin
    {语句部分}
end

  块的声明部分可以包含任意顺序的变量、常量(包括资源字符串)、类型、过程、函数和标签的声明。在程序块的声明部分,还可以包含一到多个exports子句。例如,一个函数的声明如下所示。

01 function UpperCase(const S : String): String;
02 var
03     Ch : Char;
04     L : Integer;
05     Source, Dest : PChar;
06 begin
07     //此处省略部分代码...
08 end;

代码3.13 一个函数的声明

  在代码3.13的第1行是一个函数头,随后的部分就是一个块。Ch、L、Source和Dest是局部变量。它们仅在UpperCase的函数块中被声明和使用。同时,它们会覆盖那些在程序块或者是单元的接口部分或实现部分中声明的同名标识符。

3.11 作用域

  一个声明后的标识符(如变量或函数)只能在作用域(scope)中使用。声明的位置决定了标识符的作用域。在程序、函数和过程中声明的标识符,其作用域限制在声明它们的块中。在单元的接口部分中声明的标识符,其作用域包含了从声明发生到单元结束,以及引用了该单元的其他任意单元或程序。在函数和过程中声明的标识符,其作用域较窄,通常称之为局部(local)的。而作用域广泛的标识符,通常称之为全局(global)的。决定了标识符作用域的规则总结如下。

声明的位置 标识符的作用域
在程序、函数或过程的声明部分。 从声明发生到当前块的结束,以及包括在作用域内的其他所有的块。
在单元的接口部分。 从声明发生到单元结束,以及引用了该单元的其他任意单元或程序。
在单元的实现部分,但不在任何函数或过程块内。 从声明发生到单元结束。该标识符可以用于单元中包括在作用域内的任意一个函数或过程,以及单元的初始化部分和终止化部分(如果存在)。
在记录类型的定义中(即,该标识符是记录中的一个字段)。 从声明发生到记录类型定义的结束。关于记录类型的详细介绍请参考第4.10节。
在类类型的定义中(即,该标识符是类中的一个数据字段、属性或方法)。 从声明发生到类类型定义的结束,同时包括该类的子类以及该类和子类中的所有方法的块。关于类类型的详细介绍请参考第9节。

表3.1 作用域的规则

3.12 命名冲突

  当两个不同意义的同名标识符的作用域相互重叠时就会出现命名冲突(naming conflict)。编译器会按照一定的规则来解决命名冲突。

  当一个块包围了另一个块时,我们称前者为外层块,后者为内层块。如果在外层块中声明的标识符在内层块中被重新声明,那么内层块中的声明将优先于外层块中的声明,并且根据该标识符在内层块中的声明来确定它意义。例如,假设在单元的接口部分声明了一个名为MaxValue的全局变量,然后在该单元内的函数中声明了一个同名的局部变量。那么在不使用限定标识符的情况下,在该函数块中访问到的MaxValue变量就是在该块中声明的局部变量。类似的,当一个函数包围另一个函数时,内层函数声明的标识符相当于创造了一个新的标识符。在外层函数声明的标识符的作用域内,内层函数可以对这个标识符进行重新声明。注意一个原则,内层中声明的标识符总是优先于外层中声明的标识符。

  如果引用了多个单元,那么作用域的定义将会进一步的复杂化。在uses子句中列出的每一个单元都引入了一个新的作用域,该作用域作为外层包围了剩余的单元以及使用该uses子句的程序或单元的作用域。在uses子句中的第一个单元代表了最外层的作用域,其后的每一个单元都代表了一个新的相对于前一个的内层的作用域。如果两到多个单元在它们的接口部分声明了同名的标识符,那么在不使用限定标识符的情况下,访问到的标识符是在最内层作用域中声明的那个标识符。也就是说,先在本单元的作用域内寻找该标识符,如果在本单元内没有声明该标识符,那么从uses子句中列出的最后一个单元开始,逐一向前寻找该标识符。

  System和SysInit单元会被自动添加到每一个程序或单元中。在System单元中声明了一些由编译器自动解释的预定义类型、例程和常量。System单元总是具有最外层的用作域。

  使用限定标识符可以绕开内层声明的标识符去访问外层声明的同名标识符,关于限定标识符的详细介绍请参考第3.4节。使用with语句也可以达到相同的效果,关于with语句的详细介绍请参考第7.5节。

  评论这张
 
阅读(328)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017