# 第二章
## 编写Pascal代码
进入正题前先谈一下Pascal代码编写风格的问题。“除了遵循语法规则外,你应该怎样来写代码呢?” 关于这个问题各人答案会有不同,因为各人喜欢的风格不同。总的来说,任何编码风格的目标都是使代码清楚、明晰,采用某种风格和格式只是一种简略方法,用于更清楚地表达你的代码要干什么。实现代码清楚明晰的基本原则是保持代码的一致性,也就是无论选用哪种风格,在整个工程中要始终保持同一风格。
### 注释
在Pascal中,注释括在大括号中或带星号的圆括号中。Delphi 也认可C++ 风格的注释,即把注释放在双斜线后。例如
* * * * *
~~~
{this is a comment}
(* this is another comment *)
// this is a comment up to the end of the line
~~~
* * * * *
第一种注释方式较简略,使用也较为普遍;第二种方式在欧洲使用较广 ,因为欧洲的键盘缺少大括号;第三种方式的注释是从C++借用来的,只在32位版本的Delphi中可用,它在给一行代码加短注释时非常有用。
在这本书中我用斜体表示注释,用粗体表示关键词,以此与默认的Delphi语法风格表示一致。
上述三种不同的注释方式有益于进行嵌套注释。例如你要注销一段代码,而代码行中又包含真正的注释行,这时采用同一种注释方式是不对的:
~~~
{ ... code
{comment, creating problems}
... code }
~~~
正确的方法是插入第二种注释方式:
~~~
{ ... code
//this comment is OK
... code }
~~~
注意:如果左大括号或圆括号-星号后面跟美元符号($),那么其中的内容就成了编译指令,如 {$X+}。
实际上,编译指令仍是注释。例如,{$X+ This is a comment} 是合法的。这既是有效的编译指令又是一条注释,尽管明智的程序员很可能会注意把编译指令和注释分开。
### 使用大写字母
Pascal 编译器(不象其他语言的编译器)不考虑字符的大小写,因此标识符Myname、 MyName、 myname、 myName、 和MYNAME是完全相同的。总体上来说,这是Pascal的一大优点,因为在大小写敏感的语言中,许多语法错误是由不正确的大写引起的。
注意:Pascal语言的大小写不敏感特性有一个例外:控件包中的Register 过程必须以大写字母R开始,因为需要与C++Builder 兼容。
然而大小写不敏感也有不便之处:第一,你必须注意大小写不一致的标识符实际上是相同的,以避免把他们当成不同的元素使用;第二,你必须尽量保持大写使用的一致性,以提高代码的可读性。
大写使用的一致性不是编译器强制要求的,但是保持大写使用的一致性是值得提倡的好习惯。一个常用的方法是将每个标识符的第一个字母大写,标识符若由几个词组合而成(中间不能插入空格),每个词的第一个字母应大写:
~~~
MyLongIdentifier
MyVeryLongAndAlmostStupidIdentifier
~~~
此外,编译器不编译代码中的空格、空行和Tab键空格,这些元素通称为空白,它们只用来提高代码的可读性,不影响编译过程。
不同于BASIC, Pascal 语句允许分行书写,即将一条长指令分割成两个或更多的代码行。允许语句分行的缺点(至少对许多BASIC程序员)是:语句结束时不能忘了加分号,更确切地说,必须记着把语句和紧接它的语句分开。语句分行唯一的限制是字符串不能跨行。
关于空格和语句分行的使用没有既定的规则,以下是几点经验:
Delphi 代码编辑器中有一条竖线叫右边线(Right Margin),你可以把右边线设置在60或70个字符处。如果以这条线为基准,代码不超过这条界限,那么打印到纸上的代码看起来会很好看。否则,打印时长语句会被随意分行,甚至在一个词的中间断开。
当一个函数或过程有多个参数,通常的做法是把各参数放在不同的行上。
你可以在注释行前留一行空白,或把长的代码句分成较小的部分,这样能提高代码的可读性。
用空格隔开函数调用的参数,表达式中的运算符也最好用空格隔开。一些程序员可能会对这些提议不以为然,但我坚持认为:空格是免费的,你不必为使用空格付费,何乐而不为呢?
优化版面
关于代码编写风格的最后一条建议是:尽量使用空白优化版面。这一条很容易做到,只需要在写复合句时,以上一句为参照,下一句向右缩进两个空格,复合句内嵌的复合句缩进四个空格,依此类推。例如:
~~~
if ... then
statement;
if ... then
begin
statement1;
statement2;
end;
if ... then
begin
if ... then
statement1;
statement2;
end;
~~~
相似的缩进格式常用于变量或数据类型声名区,也可用于语句的续行:
~~~
type
Letters = set of Char;
var
Name: string;
begin
{ long comment and long statement, going on in the
following line and indented two spaces }
MessageDlg ('This is a message',
mtInformation, [mbOk], 0);
~~~
提出以上代码编写格式只是向你建个议而已,这样代码能更加易读,其实代码格式并不影响编译结果。在本书的例子和代码段中我始终坚持使用上述代码风格,Delphi 中的源代码、手册和帮助例子均采用了相似的格式化风格。
### 突出Pascal元素
为了使Pascal 代码更易读写,Delphi 编辑器中增加了Pascal 元素的色彩设置功能,也就是编辑器会用不同的颜色表示不同的Pascal 元素。缺省情况下,关键字以粗体表示,字符串和注释用蓝色表示(并且常常是斜体)。
用不同色彩显示不同的Pascal 元素对保留字、注释和字符串十分有利,因为着色后你一眼就可以看出拼错的关键字、没有正常结束的字符串及多行注释。
使用编辑器环境选项对话框中的色彩(Color)页,很容易就能定制各种Pascal 元素的色彩(见图2.1)。如果独自工作,那么你可随意选择喜欢的颜色。如果是与其他程序员合作,那么应该使用大家统一的标准颜色。我感觉在同一台计算机上使用我不习惯的色彩配置确实很难受。
![](https://box.kancloud.cn/601e208bedadbd3f237d5f1ef1e84bc8_443x418.png)
**图2.1 编辑环境设置对话框**
注意:本书中我选用了一种色彩方案来显示源代码清单,希望能使代码更易读。
### 使用代码模板
Delphi 3 中增加了用于代码编辑的新功能“代码模板”。由于写Pascal 语句时,常常会重复键入相同的一组关键字,为此Borland 公司开发了名为“代码模板”的新功能,代码模板中存放了能与代码缩略形式对应的完整代码,你输入缩略代码,然后按Ctrl+J,完整的代码就出现了。例如,你输入arrayd,然后按Ctrl+J,Delphi 编辑器会把你的文本扩展为:
~~~
array [0..] of ;
~~~
由于同一种代码结构在预定义的代码模板中通常有多种样式,所以模板中的缩略形式一般加有一个后缀字母,以便你选用。此外,你也可以只输入缩略形式的头几个字母,如你输ar,然后按Ctrl+J,那么,编辑器中会弹出一个菜单,菜单中列出了代码缩略形式选项,见图2.2所示。
![](https://box.kancloud.cn/22a1c6f313d38680224fb74194032f45_592x265.png)
**图2.2 代码模板选项**
代码模板可以定制,就是你可以修改已有的模板也可以添加自己常用的代码段。用代码模板输入的代码文本中通常会出现‘|’字符,它表示输入模板代码后光标应跳到的位置,就是说你应该从这个光标位置开始输入,写完这句代码。
### 编程语句
标识符一经定义 ,你就可以在语句及组成语句的表达式中使用它们。Pascal 提供了许多语句和表达式,首先来看看关键字、表达式和运算符。
### 关键字
关键字是Object Pascal 的保留标识符,在语言中有着特殊含义。保留字不能用作标识符,指令字也同样不应该用作标识符,即使编译器允许也最好不用。在实际中你不应该把任何关键字用作标识符。
表2.1是面向对象 Pascal 语言(Delphi 4)中特殊标识符的完整列表,其中包括关键字及保留字。
**表2.1:面向对象Pascal语言中的关键字及保留字**
|关键字 | 作用 |
| --- | --- |
|absolute | 指令 (变量)|
|abstract |指令 (方法) |
|and | 运算符 (布尔)|
|array| 类型|
|as |运算符 (RTTI)|
|asm |语句 |
|assembler | 向后兼容 (汇编) |
|at |语句 (异常处理) |
|automated |访问类别符 (类)|
|begin | 块标记 |
|case |语句|
|cdecl | 函数调用协定 |
|class | 类型|
|const | 声明或指令(参数)|
|constructor| 特殊方法|
|contains |运算符 (集合) |
|default |指令 (属性) |
|destructor | 特殊方法 |
|dispid | dispinterface界面类别符 |
|dispinterface |类型 |
|div |运算符|
|do |语句|
|downto | 语句 (for)|
|dynamic | 指令 (方法)|
|else | 语句 (if 或 case) |
|end |块标记 |
|except |语句 (异常处理) |
|export |向后兼容 (类)|
|exports | 声明 |
|external |指令 (函数) |
|far | 向后兼容 (类) |
|file |类型 |
|finalization| 单元结构|
|finally |语句 (异常处理)|
|for |语句|
|forward | 函数指令 |
|function |声明 |
|goto | 语句|
|if | 语句|
|implementation| 单元结构 |
|implements | 指令 (属性) |
|in |运算符 (集合) - 工程结构 |
|index |指令 (dipinterface界面) |
|inherited |语句|
|initialization |单元结构|
|inline |向后兼容 (见 asm)|
|interface |类型|
|is |运算符 (RTTI) |
|label | 声明|
|library | 程序结构|
|message |指令 (方法) |
|mod | 运算符 (数学)|
|name | 指令 (函数)|
|near | 向后兼容 (类)|
|nil | 数值 |
|nodefault | 指令 (属性) |
|not |运算符 (布尔) |
|object |向后兼容 (类) |
|of | 语句 (case) |
|on |语句 (异常处理)|
|or |运算符 (布尔)|
|out |指令 (参数)|
|overload| 函数指令 |
|override| 函数指令 |
|package | 程序结构 (控件包)|
|packed | 指令 (记录) |
|pascal | 函数调用协定 |
|private | 访问类别符 (class) |
|procedure| 声明 |
|program |程序结构 |
|property |声明 |
|protected |访问类别符 (类)|
|public |访问类别符 (类)|
|published |访问类别符 (类)|
|raise |语句 (异常处理) |
|read |属性类别符 |
|readonly |dispatch界面类别符 |
|record | 类型 |
|register |函数调用协定 |
|reintroduce |函数指令 |
|repeat | 语句|
|requires |程序结构 (控件包) |
|resident |指令 (函数) |
|resourcestring| 类型|
|safecall | 函数调用协定|
|set |类型|
|shl | 运算符 (数学)|
|shr | 运算符 (数学)|
|stdcall| 函数调用协定 |
|stored | 指令 (属性) |
|string |类型 |
|then | 语句 (if) |
|threadvar |声明 |
|to |语句 (for) |
|try |语句 (异常处理) |
|type |声明|
|unit |单元结构|
|until |语句 |
|uses |单元结构 |
|var |声明 |
|virtual |指令 (方法) |
|while |语句 |
|with | 语句 |
|write |属性类别符|
|writeonly |dispatch 界面类别符 |
|xor |运算符 (布尔) |
### 表达式和运算符
建立表达式没有通用的方法,因为要取决于所用的运算符,Pascal包括有逻辑运算符、算术运算符、布尔运算符、关系运算符和集合运算符等等。表达式可用于确定赋给一个变量的值、计算函数或过程的参数、或者判断一个条件,表达式也可以包含函数调用。表达式是对一个标识符的值而不是标识符本身进行运算。
所有编程语言中的表达式都是常量、变量、数值、运算符和函数值的合法组合。表达式可以传递给过程或函数的值参,但不能传递给过程或函数中的引用参数。
### 运算符及其优先级
如果你以前写过程序,那么你已经知道表达式是什么了。这里我专门讲一下Pascal 运算符的特殊部分:运算符的优先级。表2.2中按优先级分组列出了Pascal语言的运算符。
> 与大多数编程语言相反,Pascal语言中and和or运算符的优先级比关系运算符高。因此,如果你的代码为a < b and c < d,编译器首先会编译and运算符,由此导致编译出错。为此你应该把每个 < 表达式用小括号括起来: (a < b) and (c < d)。
同一种运算符用于不同数据类型时它的作用不同。例如,运算符 + 可以计算两个数字的和、连接两个字符串、求两个集合的并集、甚至给PChar 指针加一个偏移量。然而,你不能象在C语言中那样将两个字符相加。
另一个特殊的运算符是 div。在Pascal 中,你能用 / 计算两个数字(实数或整数)的商,而且你总能得到一个实型结果。如果计算两个整数的商并想要一个整型结果,那么就需要用 div 运算符。
**表 2.2: Pascal语言中的运算符及其优先级**
**单目运算符 (最高优先级**)|
|@ |取变量或函数的地址(返回一个指针) |
| --|-- |
|not |逻辑取反或按位取反 |
**乘除及按位运算符**
|*| 相乘或集合交集 |
| --|-- |
|/| 浮点相除 |
|div |整数相除 |
|mod |取模 (整数相除的余数) |
|as| 程序运行阶段类型转换 (RTTI运算符) |
|and |逻辑或按位求和 |
|shl |按位左移 |
|shr |按位右移 |
加减运算符
|+|相加、集合并集、字符串连接或指针增加一个偏移量|
| --|-- |
|- |相减、集合差集或指针减少一个偏移量 |
|or |逻辑或按位或运算|
|xor| 逻辑或按位异或运算|
关系及比较运算符(最低优先级)
|= |判断是否相等|
| --|-- |
|<>| 判断是否不相等 |
|<| 判断是否小于 |
|>| 判断是否大于 |
|<= |判断是否小于或等于,或是否是一个集合的子集|
|>= |判断是否大于或等于,或是否是一个集合的父集|
|in| 判断是否是集合成员 |
|is| 判断对象是否类型兼容 (又一个RTTI运算符) |
### 集合运算符
集合运算符包括并(+)、差(-)、交(*)、成员检测(in),及一些关系运算符。要把一个元素添加到集合中,你可以采用集合并运算。下面是一个选择字体的Delphi 例子:
~~~
Style := Style + [fsBold];
Style := Style + [fsBold, fsItalic] - [fsUnderline];
~~~
另一种方法是利用标准过程Include 和Exclude,它们效率更高(但不能用于控件的集合类型属性,因为只能操纵一个元素):
~~~
Include (Style, fsBold);
~~~
* * * * *
### 结束语
从上面内容我们已经了解了Pascal 程序的基本布局,下面开始探究它的细节。先从预定义和自定义数据类型开始,然后是利用关键词组织编程语句。