Delphi1&2 工程文件及语法基础

第一章 工程文件介绍

1.1 工程文件 .dpr

Delphi程序由多个unit模块组成,这些模块被一个单一的源文件——工程文件所联系,格式为.dpr;

.dpr的组成:程序头,[users 从句],主程序块;

  • 程序头

编写的程序的名称,与.dpr文件名相同;

  • uses从句

程序主文件中使用到的所有.pas文件的名称,就像java的import;

  • 主程序块

用begin,end包裹的代码段,工程文件中的全局标识符必须声明于begin之前;

1.2 子模块 .pas

程序的子模块称为一个单元,格式为.pas;

相当于类,包含单元名称,Interface关键字,implementation,[initialization],[finalization],关键字end; 举例:

unit Unitl;
interface
 uses...
...
implementation
 uses...
...
initialization
...
finalization
...
end.

一个.dpr不能引用两个名称相同的.pas;

implementation部分也可以定义常量,变量,例程等,但是只能在本单元内使用;

finalization相当于是finally块,程序退出时运行此部分的代码;

1.3 常见程序类型

常见程序类型:命令行程序、窗体程序;

命令行程序
program Project1;
{$APPTYPE CONSOLE}
uses
 SysUtils;
var
 x,y:integer;
begin
 x:=9;
 y:=7;
 writeln(x+y);
 readln;
end.

注:readln用于使屏幕暂停,按回车键可以结束暂停;

窗体程序

在Delphi中,窗体程序可以通过直接拖动组件组成窗体,然后再手动调整生成的程序内容,比较便捷;

下面是Unit1.pas文件的代码;

unit Unit1;
interface
uses
 Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs;
type
 TForm1 = class(TForm)
 private
 {Private declarations}
 public
 {Public declarations}
 end;

var
 Form1:TForm1;
implementation
{$R *.dfm}
end.
源文件.dpr的代码如下
program Project1;
uses
 Forms,
 Unit1 in 'Unit1.pas'{Form1};
{$R *.res}
begin
 Application.Initialize;
 Application.MainFormOnTaskbar := True;
 Application.CreateForm(TForm1, Form1);
 Application.Run;
end.

第二章 语法基础

2.1标识符

标识符分为标准标识符和自定义标识符;

标准标识符包括:

1.标准常量及变量名称,如FALSE,TRUE等

2.标准类型名称,如Integer,Real等

3.标准例程名称,如Sin,IntToStr等

4.标准文件名称,如标准I/O名称Input,Output等

5.Delphi2010的保留字和指令符,如procedure,class等

自定义标识符需要满足以下条件:

1.Delphi语言不区分大小写,标识符亦然

2.自定义标识符不能与当前域中的其他标识符相同

3.长度小于255字符,超过部分将被舍弃

4.只能由英文字母,下划线和数字组成,不能包含空格,第一个字符不能是数字

2.2特殊符号

单个符号
#$&'()*+,-./:;<=>@[]^{}

组合符号
(* 
(. 
*) 
.. 
// 
:= 
<= 
>= 
<>

(.等效于[
.)等效于]
(*等效于{
*)等效于}

除此之外,%?\!"|~也不能用于标识符

2.3标识符的作用域

局部标识符是指定义于例程(函数或者过程)中的标识符,此种标识符只能定义他们的例程;

除此之外都是全局标识符,全局标识符分为公有和私有标识符;

定义于.pas文件的Interface部分是公有,其他为私有;

公有标识符不但在本.pas文件中有效,在所有引用了本.pas文件的其他文件如.dpr文件中也有效;

2.4保留字

and,else,inline,property,try,

array,end,interface,raise,type,

as,except,is,record,unit,

at,exports,label,remove,until,

asm,file,library,repeat,uses,

begin,finalization,mod,resourcestring,var,

case,finally,nil,set,while,

class,for,not,shl,with,

const,function,on,shr,xor,

constructor,goto,of,strict private,

destructor,if,or,strict protected,

dispinterface,implementation,out,string,

div,in,packed,then,

do,inherited,procedure,threadvar,

downto,initialization,program,to

2.5指令符

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

注:private,protected,public,published,automated在定义一个类时,被视作保留字,在其他场合被视为指令符;

2.6常量

直接常量、声明常量、Delphi本身预定义的常量;

直接常量:

整型常量、实型常量(即浮点数、小数)、字符及字符串型常量、布尔型常量;

符号常量
Const
    CONS_A = 123;
    CONS_B = CONS_A+234;
    CONS_C = CONS_A+CONS_B+89;

编译器在编译时根据值的类型推测常量的类型,如果

CONS_B=CONS_A+'delphi';

将编译错误,字符串型常量与数值型常量不能相加;

资源字符串
resourcestring
    str='The Current Edition is Delphi2010';

这种方式声明的字符串用于程序中时将被编译到资源文件中,这样的好处是可以任意修改字符串而不需要重载编译程序;

类型常量
Const
    <标识符1>:<类型1>=<常量值1>;

不能将常量声明为文件类型和变体类型;

类型常量的常量值中不能含有其他常量;

Const
    Name:string='delphi2010';
    Caption:string='my'+name;//不合法,name是另一个常量,不能在表达式中
    MultiPi:real=2*3.14;
数组常量

数组常量主要有三种:静态数组、字符串、多维数组;

记录常量
type
  TPoint = record
     X,Y:Single;
  end;
  TRec = record
     x:integer;
  case tag:integer of
     1:(i:integer);
     2:(n:integer);
  end;
const
   Origin:TPoint=(X:0.0;Y:0.0);
   VRec:TRec=(x:7;tag:1;i:2;);

注:

记录常量不能含有任何形式的文件变量;

各个字段的赋值顺序必须与声明时的顺序一致;

对于变体记录,如果其中出现了一个tag值,则必须赋值;

只有含有tag值时变体记录中的变体部分才能被赋值。

指针常量举例
var
   i,n:integer;
const
   pi:^integer = @i;

i的值可以随时改变,但是指针pi只能指向i,不能再指向其他变量;

2.7变量

变量分为静态变量和动态变量;

静态变量在定就确定所需的内存大小;

变量手动赋值前会由系统赋值;

全局变量初始化为0,如果是指针就初始化为nil;如果是字符串初始化为空字符串;

局部变量系统会随机赋值,这种赋值非常不可靠,所以局部变量一定要手动赋值后才能使用。

全局变量手动初始化举例

Var
    GlobalVar:integer=100;

共址变量举例

var
    c:AnsiChar;
    i:Byte absolute c;
begin
    c:='A';
    writeln(i);//屏幕显示65
    readln;
end.

i被声明为变量c的共址变量,这样i与c在内存中的起始位置相同;

i在内存中占用一个字节,当读取i时,系统将c的第一个字节当成i的值,故为65;

注:

1.共址变量具有相互性,如下所示,结果不变;

var
    i:Byte;
    c:AnsiChar ansolute i;//显示为65

2.可以声明多个变量为共址变量;

var
    i:Byte;
    c:AnsiChar absolute i;
    str: ShortString absolute i;

2.8类型声明

举例

type
   aSet = set of char;
   myInteger = Integer;

除此之外,还有一种声明方式

type
    myInteger = type Integer;

1.当类型表达式是另一个类型的名称,比如Integer;

如果说第一种声明是相当于一个人的名字,myInteger与Integer都是一个人的名字;

第二种声明就是两个人的名字,只不过这两个人各个方面除了名字都完全相同;

一般情况下不会被注意到,但是牵涉到数据类型的动态判断等比较高级的用法时,这种差别会导致类型不兼容;

2.当类型表达式是一个结构,如set of char;

type
    aSet = set of Char;
    bSet = set of Char;

从表面上看,aSet与bSet应当完全一致,然而并非如此,以上相当于;

type
    aSet = type bSet;

    bSet = type aSet;

如果要完全相同,则;

type
    aSet = set of Char;
    bSet = aSet;

2.9注释

//...单行注释

{...}

(*...*)

注释嵌套必须嵌套其他类型注释,如;

{XXXXX=9,I=0(*参数传递*)}
{$APPTYPE CONSOLE}

解释:

当第一个字符是$,而且是{}或者(**)包裹时,表示编译器提示符;

比如当前这句,表示当前程序是一个命令行程序;

2.10表达式

表达式必须有一个返回值,Delphi的过程调用不返回任何值,故过程调用不属于表达式,同理;

i:=3; // 不属于表达式

i>=9; // 属于表达式

注:Delphi中,表达式不能单独存在,只能位于赋值符号:=的右边;

2.11语句

2.11.1简单语句

包括赋值语句、例程调用、goto语句;

例程调用举例

对于不接受参数的过程和函数,在调用时可以省略例程名后的括号,如;

procedure M1;
...
   M1;
   ...

对于函数,调用时,可以将其返回值赋给一个变量,也可以单独调用;

 procedure M1:integer;
 ...
 var
    i:integer
 ...
    i:=M1;
    M1;
 ...

goto语句举例

 var
    i:integer;
 label 1, 2;
 begin
 1:writeln('请输入i的值:');
    readln(i);
    if i <> 0 then
       goto 1;
    exit;
 end.

输入数字i,如果i为0,自动退出,如果不为0,再次输入i;

2.11.2结构语句

包括复合语句,with语句,条件语句,循环语句等;

复合语句举例

 begin
    Z:=X;
    X:=Y;//可以省略分号X:=Y
 end;

汇编语句举例

使用asm和end包裹;

 var
    a:word;
 begin
    asm
       mov ax,43;
       add ax,54;
       mov a, ax;
    end;
    writeln(a);
    readln;
 end.

With语句举例

定义一个日期;

 type
    TDate = record
       Day:Integer;
       Month:Integer;
       Year:Integer;
    end;
 var
    OrderDate:TDate;

可以通过如下方式访问其中的字段;

 if OrderDate.Month = 12 then
 begin
    OrderDate.Month = 1;
    OrderDate.Year := OrderDate.Year+1;
 end
 else
    OrderDate.Month := OrderDate.Month+1;

但是如果运用with语句,可以简化为;

with OrderDate do
   if Month=12 then
      begin
         Month := 1;
         Year := Year+1;
   end
   else
Month := Month+1;

注:with的声明方式为;

with obj do 语句
with obj1, obj2 do 语句

注:对于with语句中出现的所有单独的标识符,编译器优先在obj中寻找其含义,即;

type
   TRec = record;
      x,y:integer;
   end;
var
   obj:TRec;
   x:integer;
begin
   with obj do
   begin
      x:=3;
   end;
   writeln(obj.x);
end.

这里的赋值是赋给了obj的x,如果想赋值给全局变量x,则;

project1.x := 3;

注:嵌套的with语句,with obj1,obj2,obj3...do...相当于;

with obj1 do
   with obj2
      ...
      with objn do
      ...

编译器会从最内层的objn开始寻找标识符,如果没找到,则在外面一层找,以此类推,直到obj1,如果还没找到,会在with语句之外寻找;

如:

type
   TInner = record
      x,y:Integer;
   end;
   TRec = record;
      x,y:integer;
      z:string;
   end;
var
   Rec:TRec;
   Inner:TInner;
   x:integer;
   z:string;
begin
   with rec, inner do
   begin
      x:=3;
      z:='delphi';
   end;
   writeln(Inner.x);
   writeln(rec.z);

2.12块和域

function UpperCase(const S:string):string;
var
   Ch:Char;
   L:Integer;
   Source, Dest:PChar;
begin
   ...
end;

以上是一个函数的声明,和四个变量的声明一起构成了块的声明部分;

注:块中声明的标识符具有局部性,如上面的四个变量都只能声明在它们的块中;

域Scope

域指的是标识符的有效范围;

声明在块中的标识符只能用在这个块,成为局部标识符;

如果是变量则是局部变量,如果是常量则是局部常量;

声明在interface部分的标识符的有效域是任何一个引用这个单元的源文件,成为全局标识符;

当一个块包含了另一个块,内部块的标识符将掩盖外部块的标识符,如:

function M1:integer;
var
   s:string;
   function M2:integer;
   var
      s:string;
   begin
      s:='delphi';
   end;
begin
end;

这里被赋值的是M2的s;

注:

uses从句中的第一个单元为最外层,但是system和sysinit单元永远是最外层;

最内层的单元并不是uses从句中最后出现的单元,而是正在使用这个标识符的单元才是最里层的单元;

多个单元在interface部分声明了同一标识符,则任何一个未限定的标识符都会被认为是最里层的标识符,

限定即Modle.Fun,表示目前引用的是Modle中的Fun,Modle可以是单元名,工程名,结构类型的变量名。

2.13兼容

类型兼容条件

当A、B两种数据类型满足以下条件时,可以将B类型的值赋给A类型的变量,这种情况成为类型兼容。

1.都是实数类型;

2.都是整数类型;

3.B是A的子界类型,如A是Integer,B是Byte;

4.同种类型的子界类型,如byte和smallint都是integer的子界类型,所以byte可以赋给smallint,反之亦可;

这种情况要注意B的值不能超出A的表示范围,否则可能会编译错误或数据丢失。

5.都是集合类型,并且基础类型兼容,如;

var
   s1:set of byte;
   s2:set of byte;

6.A是字符串类型,B是字符串、packed-string、Char类型;

7.A是Variant,B是整数、实数、字符串、字符、布尔类型,反之亦可;

8.都是类、类引用或接口类型,并且B继承A;

9.A是PChar或者PWideChar,B是0下标开始的字符数组;

array[0..n] of Char

10.A是Pointer(无类型指针),B是任意类型指针;

11.两个是同一种类型的指针,并且开启了编译器指示字{$T+};

12.两个都是过程类型,有相同的返回类型,并且参数的个数、位置和类型都相同;

赋值兼容条件

T1是一个变量,T2是一个表达式,若T2的值在T1的取值范围内,则如果以下条件至少一个成立,T2可以赋给T1;

1.T1和T2是同一种类型,并且不是文件类型或者包含文件类型的结构类型;

2.T1和T2是兼容的有序类型;

3.都是实数类型;

4.T1 是实数类型,T2是整数类型;

5.T1是PChar类型或者任何字符串类型,T2是字符串常量;

6.都是字符串类型;

7.T1是字符串类型,T2是字符或packed-string类型;

8.T1是一个长串类型,T2是一个PChar类型;

9.是兼容的packed-string类型;

10.是兼容的集合类型;

11.是兼容的指针类型;

12.都是类、类引用或接口,并且T2继承T1;

13.T1是接口,T2是实现T1的一个;

14.T1是PChar或PWideChar,T2是一个0下标开始的字符数组;

15.兼容的过程类型;

16.T1是Variant类型,T2是整数、实数、字符串、字符、布尔或接口类型;

17.T1是整数、实数、字符串、字符或布尔类型,T2是Variant;

18.T1是IUnknown或IDispatch接口类型,T2是Variant;

如果T1是IUnknown,T2类型编码必须是varEmpty、varUnknown或varDispatch;

如果T1是Idispatch,T2 的编码类型必须是varEmpty或者varDispatch;

2.14类型转换

包括值转换和变量转换;

值转换
Integer('A')
Char(65)

var
   ch:AnsiChar;
   ...
   ch:=AnsiChar(320);
   ...

这里,AnsiChar类型的值域中只有256个值(最大序数255),320超出,这时候编译器可以报错,也可以截取;

一般的截取直接取最大值255,这里的截取不一样;

首先将320除以255得到余数64,然后将64和值域中的最小值0相加,所得结果即目标值;

注:

无论是直接截取还是反绕,都不会改变赋值的符号,一个负数得到的结果一定是负数,只是绝对值不一定相同;

变量转换
var
   ch:AnsiChar;
   i:Integer;
   b:Byte;
begin
   ch:=AnsiChar(321);
   i:=320;
   b:=Byte(i);
   writeln(integer(ch));
   writeln(b);
   readln;
end.

变量转换第一规则:目标类型占用的内存永远不要小于源类型,否则编译器会进行反绕或截断。

注:

实数的类型转换较为特殊,所有类型的实数在转换到其他类型的值时,首先被转换为实数中的extended类型,然后再转成其他类型,

而extended类型过大,大部分变量无法容纳这种类型,所以将实数转换成其他类型时应当使用Delphi提供的例程。

如Int、Round、Trunc,均定义于system单元中,

Int返回一个实数的整数部分,是实数的形式,

Round返回与指定实数最接近的整数,

Trunc返回一个实数的整数部分,是整数的形式。

举例

var
   r:real;
begin
   r:=3.84;
   writeln(Int(r));
   writeln(Round(r));
   writeln(Trunc(r));
   readln;
end.