pascal语言中的过程与函数Word文档下载推荐.docx
- 文档编号:22834486
- 上传时间:2023-02-05
- 格式:DOCX
- 页数:20
- 大小:71.91KB
pascal语言中的过程与函数Word文档下载推荐.docx
《pascal语言中的过程与函数Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《pascal语言中的过程与函数Word文档下载推荐.docx(20页珍藏版)》请在冰豆网上搜索。
X,Y:
X:
=Double(StrToInt(Edit1.Text));
Y:
=Double(X);
ShowMessage(IntToStr(Y));
注意:
现在不必考虑上面两个过程的语法,实际上它们是方法。
只要把两个按钮(button)放到一个Delphi窗体上,在设计阶段单击它们,DelphiIDE将产生合适的支持代码,你只需要填上begin和end之间的那几行代码就行。
编译上面的代码,需要你在窗体中加一个Edit控件。
现在回到我前面提到过的代码封装概念。
当你调用Double函数时,你不需要知道该函数的具体实现方法。
如果以后发现了更好的双倍数计算方法,你只需要改变函数的代码,而调用函数的代码不必改变(尽管代码执行速度可能会加快!
)。
Hello过程也一样,你可以通过改变这个过程的代码,修改程序的输出,Button2Click方法会自动改变显示结果。
下面是改变后的代码:
MessageDlg('
mtInformation,[mbOK]);
提示:
当调用一个现有的Delphi函数、过程或任何VCL方法时,你应该记住参数的个数及其数据类型。
不过,只要键入函数或过程名及左括号,Delphi编辑器中会出现即时提示条,列出函数或过程的参数表供参考。
这一特性被称为代码参数(CodeParameters),是代码识别技术的一部分。
引用参数
Pascal例程的传递参数可以是值参也可以是引用参数。
值参传递是缺省的参数传递方式:
即将值参的拷贝压入栈中,例程使用、操纵的是栈中的拷贝值,不是原始值。
当通过引用传递参数时,没有按正常方式把参数值的拷贝压栈(避免拷贝值压栈一般能加快程序执行速度),而是直接引用参数原始值,例程中的代码也同样访问原始值,这样就能在过程或函数中改变参数的值。
引用参数用关键字var标示。
参数引用技术在大多数编程语言中都有,C语言中虽没有,但C++中引入了该技术。
在C++中,用符号&
表示引用;
在VB中,没有ByVal标示的参数都为引用。
下面是利用引用传递参数的例子,引用参数用var关键字标示:
procedureDoubleTheValue(varValue:
Integer);
Value:
在这种情况下,参数既把一个值传递给过程,又把新值返回给调用过程的代码。
当你执行完以下代码时:
X:
=10;
DoubleTheValue(X);
x变量的值变成了20,因为过程通过引用访问了X的原始存储单元,由此改变了X的初始值。
通过引用传递参数对有序类型、传统字符串类型及大型记录类型才有意义。
实际上Delphi总是通过值来传递对象,因为Delphi对象本身就是引用。
因此通过引用传递对象就没什么意义(除了极特殊的情况),因为这样相当于传递一个引用到另一个引用。
Delphi长字符串的情况略有不同,长字符串看起来象引用,但是如果你改变了该字符串的串变量,那么这个串在更新前将被拷贝下来。
作为值参被传递的长字符串只在内存使用和操作速度方面才象引用,但是如果你改变了字符串的值,初始值将不受影响。
相反,如果通过引用传递长字符串,那么串的初始值就可以改变。
Delphi3增加了一种新的参数:
out。
out参数没有初始值,只是用来返回一个值。
out参数应只用于COM过程和函数,一般情况下最好使用更有效的var参数。
除了没有初始值这一点之外,out参数与var参数相同。
常量参数
除了引用参数外,还有一种参数叫常量参数。
由于不允许在例程中给常量参数赋新值,因此编译器能优化常参的传递过程。
编译器会选用一种与引用参数相似的方法编译常参(C++术语中的常量引用),但是从表面上看常参又与值参相似,因为常参初始值不受例程的影响。
事实上,如果编译下面有点可笑的代码,Delphi将出现错误:
functionDoubleTheValue(constValue:
Integer):
//compilererror
=Value;
开放数组参数
与C语言不同,Pascal函数及过程的参数个数是预定的。
如果参数个数预先没有确定,则需要通过开放数组来实现参数传递。
一个开放数组参数就是一个固定类型开放数组的元素。
也就是说,参数类型已定义,但是数组中的元素个数是未知数。
见下例:
functionSum(constA:
arrayofInteger):
I:
=0;
forI:
=Low(A)toHigh(A)do
=Result+A[I];
上面通过High(A)获取数组的大小,注意其中函数返回值Result的应用,Result用来存储临时值。
你可通过一个整数表达式组成的数组来调用该函数:
X:
=Sum([10,Y,27*I]);
给定一个整型数组,数组大小任意,你可以直接把它传递给带开放数组参数的例程,此外你也可以通过Slice函数,只传递数组的一部分元素(传递元素个数由Slice函数的第二个参数指定)。
下面是传递整个数组参数的例子:
List:
array[1..10]ofInteger;
X,I:
//initializethearray
=Low(List)toHigh(List)do
List[I]:
=I*2;
//call
=Sum(List);
如果你只传递数组的一部分,可使用Slice函数,如下:
=Sum(Slice(List,5));
例OpenArr中可见到包括上面的完整代码(见图6.1)。
图6.1:
单击PartialSlice按钮显示的结果
在Delphi4中,给定类型的开放数组与动态数组完全兼容(动态数组将在第8章中介绍)。
动态数组的语法与开放数组相同,区别在于你可以用诸如arrayofInteger指令定义变量,而不仅仅是传递参数。
类型变化的开放数组参数
除了类型固定的开放数组外,Delphi还允许定义类型变化的甚至无类型的开放数组。
这种特殊类型的数组元素可随意变化,能很方便地用作传递参数。
技术上,arrayofconst类型的数组就能实现把不同类型、不同个数元素组成的数组一下子传递给例程。
如下面Format函数的定义(第七章中你将看到怎样使用这个函数):
functionFormat(constFormat:
string;
constArgs:
arrayofconst):
上面第二个参数是个开放数组,该数组元素可随意变化。
如你可以按以下方式调用这个函数:
N:
=20;
S:
='
Total:
;
Label1.Caption:
=Format('
%d'
[N]);
Label2.Caption:
Int:
%d,Float:
%f'
[N,12.4]);
Label3.Caption:
%s%d'
[S,N*2]);
从上可见,传递的参数可以是常量值、变量值或一个表达式。
声明这类函数很简单,但是怎样编写函数代码呢?
怎样知道参数类型呢?
对类型可变的开放数组,其数组元素与TVarRec类型元素兼容。
不要把TVarRec记录类型和Variant类型使用的TVarData记录类型相混淆。
这两种类型用途不同,而且互不兼容。
甚至可容纳的数据类型也不同,因为TVarRec支持Delphi数据类型,而TVarData支持OLE数据类型。
TVarRec记录类型结构如下:
type
TVarRec=record
caseByteof
vtInteger:
(VInteger:
VType:
Byte);
vtBoolean:
(VBoolean:
Boolean);
vtChar:
(VChar:
Char);
vtExtended:
(VExtended:
PExtended);
vtString:
(VString:
PShortString);
vtPointer:
(VPointer:
Pointer);
vtPChar:
(VPChar:
PChar);
vtObject:
(VObject:
vtClass:
(VClass:
TClass);
vtWideChar:
(VWideChar:
WideChar);
vtPWideChar:
(VPWideChar:
PWideChar);
vtAnsiString:
(VAnsiString:
vtCurrency:
(VCurrency:
PCurrency);
vtVariant:
(VVariant:
PVariant);
vtInterface:
(VInterface:
end;
每种记录都有一个VType域,乍一看不容易发现,因为它与实际意义的整型类型数据(通常是一个引用或一个指针)放在一起,只被声明了一次。
利用上面信息我们就可以写一个能操作不同类型数据的函数。
下例的SumAll函数,通过把字符串转成整数、字符转成相应的序号、True布尔值加一,计算不同类型数据的和。
这段代码以一个case语句为基础,虽然不得不经常通过指针取值,但相当简单,:
functionSumAll(constArgs:
Extended;
=Low(Args)toHigh(Args)do
caseArgs[I].VTypeof
=
Result+Args[I].VInteger;
ifArgs[I].VBooleanthen
=Result+1;
=Result+Ord(Args[I].VChar);
=Result+Args[I].VExtended^;
vtString,vtAnsiString:
=Result+StrToIntDef((Args[I].VString^),0);
=Result+Ord(Args[I].VWideChar);
=Result+Args[I].VCurrency^;
//case
我已在例OpenArr中加了这段代码,该例在按下设定的按钮后调用SumAll函数。
procedureTForm1.Button4Click(Sender:
Y:
=SumAll([Y*Y,'
k'
True,10.34,'
99999'
]);
ShowMessage(Format(
'
SumAll([Y*Y,'
])=>
%n'
[X]));
在图6.2中,你可以看到调用函数的输出和例OpenArr的窗体。
图6.2:
例OpenArr的窗体,当按Untype按钮出现的信息框
Delphi调用协定
32位的Delphi中增加了新的参数传递方法,称为fastcall:
只要有可能,传递到CPU寄存器的参数能多达三个,使函数调用操作更快。
这种快速调用协定(Delphi3确省方式)可用register关键字标示。
问题是这种快速调用协定与Windows不兼容,Win32API函数必须声明使用stdcall调用协定。
这种协定是Win16API使用的原始Pascal调用协定和C语言使用的cdecl调用协定的混合体。
除非你要调用外部Windows函数或定义Windows回调函数,否则你没有理由不用新增的快速调用协定。
在后面你会看到使用stdcall协定的例子,在Delphi帮助文件的Callingconventions主题下,你能找到有关Delphi调用协定的总结内容。
什么是方法?
如果你使用过Delphi或读过Delphi手册,大概已经听说过“方法”这个术语。
方法是一种特殊的函数或过程,它与类这一数据类型相对应。
在Delphi中,每处理一个事件,都需要定义一个方法,该方法通常是个过程。
不过一般“方法”是指与类相关的函数和过程。
你已经在本章和前几章中看到了几个方法。
下面是Delphi自动添加到窗体源代码中的一个空方法:
procedureTForm1.Button1Click(Sender:
{heregoesyourcode}
Forward声明
当使用一个标识符(任何类型)时,编译器必须已经知道该标识符指的是什么。
为此,你通常需要在例程使用之前提供一个完整的声明。
然而在某些情况下可能做不到这一点,例如过程A调用过程B,而过程B又调用过程A,那么你写过程代码时,不得不调用编译器尚未看到其声明的例程。
欲声明一个过程或函数,而且只给出它的名字和参数,不列出其实现代码,需要在句尾加forward关键字:
forward;
在后面应该补上该过程的完整代码,不过该过程代码的位置不影响对它的调用。
下面的例子没什么实际意义,看过后你会对上述概念有所认识:
procedureDoubleHello;
ifMessageDlg('
Doyouwantadoublemessage?
mtConfirmation,[mbYes,mbNo],0)=mrYesthen
DoubleHello
else
Hello'
上述方法可用来写递归调用:
即DoubleHello调用Hello,而Hello也可能调用DoubleHello。
当然,必须设置条件终止这个递归,避免栈的溢出。
上面的代码可以在例DoubleH中找到,只是稍有改动。
尽管forward过程声明在Delphi中不常见,但是有一个类似的情况却经常出现。
当你在一个单元(关于单元的更多内容见下一章)的interface部分声明一个过程或一个函数时,它被认为是一个forward声明,即使没有forward关键字也一样。
实际上你不可能把整个例程的代码放在interface部分,不过你必须在同一单元中提供所声明例程的实现。
类内部的方法声明也同样是forward声明,当你给窗体或其组件添加事件时,Delphi会自动产生相应的代码。
在TForm类中声明的事件是forward声明,事件代码放在单元的实现部分。
下面摘录的源代码中有一个Button1Click方法声明:
TForm1=class(TForm)
ListBox1:
TListBox;
Button1:
TButton;
procedureButton1Click(Sender:
过程类型
ObjectPascal的另一个独特功能是可定义过程类型。
过程类型属于语言的高级功能,Delphi程序员不会经常用到它。
因为后面章节要讨论相关的内容(尤其是“方法指针”Delphi用得特别多),这里不妨先了解一下。
如果你是初学者,可以先跳过这部分,当学到一定程度后再回过头阅读这部分。
Pascal中的过程类型与C语言中的函数指针相似。
过程类型的声明只需要参数列表;
如果是函数,再加个返回值。
例如声明一个过程类型,该类型带一个通过引用传递的整型参数:
IntProc=procedure(varNum:
这个过程类型与任何参数完全相同的例程兼容(或用C语言行话来说,具有相同的函数特征)。
下面是一个兼容例程:
在16位Delphi中,如果要将例程用作过程类型的实际值,必须用far指令声明该例程。
过程类型能用于两种不同的目的:
声明过程类型的变量;
或者把过程类型(也就是函数指针)作为参数传递给另一例程。
利用上面给定的类型和过程声明,你可以写出下面的代码:
IP:
IntProc;
IP:
=DoubleTheValue;
=5;
IP(X);
这段代码与下列代码等效:
上面第一段代码明显要复杂一些,那么我们为什么要用它呢?
因为在某些情况下,调用什么样的函数需要在实际中决定,此时程序类型就很有用。
这里不可能建立一个复杂的例子来说明这个问题,不过可以探究一下简单点的例子,该例名为ProcType。
该例比前面所举的例子都复杂,更接近实际应用。
如图6.3所示,新建一个工程,在上面放两个radio按钮和一个push按钮。
例中有两个过程,一个过程使参数的值加倍,与前面的DoubleTheValue过程相似;
另一个过程使参数的值变成三倍,因此命名为TripleTheValue
图6.3:
例ProcType窗体
procedureTripleTheValue(varValue:
=Value*3;
Valuetripled:
+IntToStr(Value));
两个过程都有结果显示,让我们知道他们已被调用。
这是一个简单的程序调试技巧,你可以用它来检测某一代码段是否或何时被执行,而不用在代码中加断点。
当用户按Apply按钮,程序会根据radio按钮状态选择执行的过程。
实际上,当窗体中有两个radio按钮时,你只能选择一个,因此你只需要在Apply按钮的OnClick事件中添加代码检测radio按钮的值,就能实现程序要求。
不过为了演示过程类型的使用,我舍近求远选择了麻烦但有趣的方法:
只要用户选中其中一个radio按钮,按钮对应的过程就会存入过程变量:
procedureTForm1.DoubleRadioButtonClick(Sender:
当用户按Apply按钮,程序就执行过程变量保存的过程:
procedureTForm1.ApplyButtonClick(Sender:
为了使三个不同的函数能访问IP和X变量,需要使变量在整个窗体单元中可见,因此不能声明为局部变量(在一个方法中声明)。
一个解决办法是,把这些变量放在窗体声明中:
...
private
{Privatedeclarations}
学完下一章,你会更清楚地了解这段代码的意思,目前只要能知道怎样添加过程类型定义、怎样修改相应的代码就行了。
为了用适当的值初始化上面代码中的两个变量,你可以调用窗体的OnCreate事件(激活窗体后,在ObjectInspector中选择这一事件,或者双击窗体)。
此外最好仔细看一看上例完整的源代码。
在第九章的Windows回调函数一节,你能看到使用过程类型的实例
函数重载
重载的思想很简单:
编译器允许你用同一名字定义多个函数或过程,只要它们所带的参数不同。
实际上,编译器是通过检测参数来确定需要调用的例程。
下面是从VCL的数学单元(MathUnit)中摘录的一系列函数:
functionMin(A,B:
overload;
Int64):
Int64;
Single):
Single;
Double):
Do
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- pascal 语言 中的 过程 函数
![提示](https://static.bdocx.com/images/bang_tan.gif)