第一章 工程文件介绍
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.