delphi的一些语法知识 以及参数传递问题,按引用方式传递参数,按值方式传递参数

   2023-02-09 学习力0
核心提示://delphi中exit,abort,break,continue 的区别exit: 退出函数体abort: 遇到异常,安静处理,就是不显示不提示break: 退出当前循环体,包括for ,while, repeat等循环体continue: 结束循环内的本次处理,继续从循环体的开始位置继续执行 Exit 是跳出当前代码块,也

//delphi中exit,abort,break,continue 的区别

exit: 退出函数体
abort: 遇到异常,安静处理,就是不显示不提示
break: 退出当前循环体,包括for ,while, repeat等循环体
continue: 结束循环内的本次处理,继续从循环体的开始位置继续执行

 

Exit 是跳出当前代码块,也就是当前函数,跳出后是要继续向下执行的(如果有后续代码)。

Abort 是从 EAbort 过来的,可以激发 exception,其实质就是 Abort = RaiseException(),是一个不出现对话框的异常。所以 Abort 的行为和异常是一样的,其代码执行顺序也是follow异常的流程。
例如:
try
(1) //执行了
abort;
(2) //不执行
exception
(3) //执行了
end;

用 Abort 能够执行 exception 里边的代码,但是如果用 Exit,就直接离开,不管 exception。

例如: 
procedure p1;
begin
p2;
p3;
end;
procedure p2;
begin
abort; //exit;
end;
procedure p3;
begin
//showmessage()..
end;

如果用 Abort,则执行不到 P3,如果用 Exit 就能够执行到 P3

******************************************************************************

在Delphi过程、函数中传递参数几个修饰符为Const、Var、Out。另一种不加修饰符的为默认按值传递参数。
一、默认方式以值方式传递参数
procedure TForm1.ProcNormal(Value: string);
begin
OrigNum:=Value+' Me';
lblReturn.Caption:=OrigNum;//OrigNum为'Hello Me'
lblOrig.Caption:=Value;//Value为'Hello'
end;

调用:
OrigNum:='Hello';
ProcNormal(OrigNum);

二、以Const方式传递参数,这个参数在调用过程中不能改变,并且这种方式会被编译器优化,一般建议尽可能地使用这种方式。
procedure TForm1.ProcConst(const Value: string);
begin
OrigNum:=Value+' Me';
lblReturn.Caption:=OrigNum;//为'Hello Me‘
lblOrig.Caption:=Value;//为'Hello Me'
end;

三、按引用方式传递参数
procedure TForm1.ProcRef(var value: string);
begin
OrigNum:=Value+' Me';
lblReturn.Caption:=OrigNum;//为'Hello Me‘
lblOrig.Caption:=Value;//为'Hello Me'
end;

四、按Out方式传递参数,这个方式传递参数时,参数可以不被初始化,即使有值也被忽视,它一般用于输出,它可以实现在一个过程中返回多个值,我们通常在分布式对象模型,如COM中使用它。
procedure TForm1.ProcOut(out Value: string);
begin
OrigNum:=Value+' Me';
lblReturn.Caption:=OrigNum;//为'Me'
lblOrig.Caption:=Value;//为'Me'
end;

五、无类型参数,这是一种较为特殊的方法,参数的类型不确定,只能用Const、Var、Out修饰,不能用于值方式传递参数,具体使用示例如下:
procedure TForm1.ProcUntype(const Value);
begin
OrigNum:=string(Value)+' Me';
lblReturn.Caption:=OrigNum;//为'Hello Me'
lblOrig.Caption:=string(Value);//为'Hello Me'
end;

六、默认参数,即如果此参数在调用时未提供时,将使用默认值。
procedure TForm1.ProcDefault(const Value, constDefaultValue:string=' 123');
begin
OrigNum:=Value+' Me'+DefaultValue;
lblReturn.Caption:=OrigNum;//为'Hello Me 123'
lblOrig.Caption:=Value;// 为'Hello Me 123'
end;

七、开放数组参数,即参数数组的元素个数不确定。
procedure TForm1.ProcArray(const Value: array of string);
var
i:Integer;
begin
for i:=Low(Value) to High(Value) do
OrigNum:=OrigNum+Value[i];//调用后为'Hello abc dbd'
lblReturn.Caption:=OrigNum;
end;

调用:

OrigNum:='Hello';
ProcArray([' abc ',' dbd']);

八、无类型开放数组参数,即类型及元素个数皆不确定。在WIN32平台中,这个参数的类型实际为array ofTVarRec,其使用示例如下:
procedure TForm1.ProcArrayConst(const Value: array of const);
var
i:Integer;
begin
for i:=Low(Value) to High(Value) do
with Value[i] do
case VType of
vtAnsiString: OrigNum:= OrigNum+String(VAnsiString);
vtInteger: OrigNum:=OrigNum+IntToStr(VInteger);
vtBoolean: OrigNum := OrigNum + BoolToStr(VBoolean);
vtChar: OrigNum := OrigNum + VChar;
vtExtended: OrigNum := OrigNum + FloatToStr(VExtended^);
vtString: OrigNum := OrigNum + VString^;
vtPChar: OrigNum := OrigNum + VPChar;
vtObject: OrigNum := OrigNum + VObject.ClassName;
vtClass: OrigNum := OrigNum + VClass.ClassName;
vtCurrency: OrigNum := OrigNum + CurrToStr(VCurrency^);
vtVariant: OrigNum := OrigNum + string(VVariant^);
vtInt64: OrigNum := OrigNum + IntToStr(VInt64^);
end;
lblReturn.Caption:=OrigNum;//调用后为'Hello abc 3'
end;

调用:

OrigNum:='Hello';
ProcArrayConst([' abc ',3]);

以上就是常见几种传递参数的方式。

//另一篇文章关于delphi参数传递的//

 
delphi参数传递
 
参数传递
    声明/实现一个过程使用的参数称为形式参数(简称形参),调用过程时传入的参数称为实际参数(简称实参)。
 
{ Info是形参}
 
procedure ShowInfo(Info: String);
begin
  ShowMessage(Info);
end;
 
var
  S: String;
begin
  S := 'lxpbuaa';
  {S是实参}
  ShowInfo(S);
end;
 
参数传递分两种:按值(by val)和引用(by ref)。这两种方式的本质区别是:
按值传递时,形参和实参是两个变量,它们开始时的值是相同的,即实参的数据被拷贝一份传递给了形参。所以此时,形参的改变不会影响到实参。
引用传递时,形参和实参是同一个变量,可以将它们之一看做是另一个的别名。 所以此时,形参改变时,实参跟着改变。
默认情况下,参数是按值传递的,传递的是数据拷贝;如果加了var前缀,则成了引用传递。
 
我们看如下例子:
procedure TForm1.ByVal(I: Integer);     {按值传递I}
begin
  ShowMessage(IntToStr(Integer(@I)));    
  {取得形参所在地址。你会发现它和实参地址是不同的,因为此时实参和形参是不同的两个变量}
  I := I + 1;
end;
 
procedure TForm1.ByRef(var I: Integer); {引用传递I}
begin
  ShowMessage(IntToStr(Integer(@I)));    
  {取得形参所在地址。你会发现它和实参地址是相同的,因为此时实参和形参是同一个变量}
  I := I + 1;
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
begin
  I := 1;
  ShowMessage(IntToStr(Integer(@I)));        {取得实参所在地址}
  ByVal(I);  { I =1}
 Showmessage(i); {i:=1;实参没有变}
  ByRef(I);  { I =2}
 showmessage(i); {i:=2,实参改变了}
end;
 
按值传递的参数可以指定默认值,比如上面的ByVal可以是这样:
procedure ByVal(I: Integer = 0);
调用它时可以省掉有默认值的参数:ByVal。带默认值的参数必须位于参数列表的最后,如:
 
procedure ByVal(I: Integer = 0; B: Boolean);
是不行的,应该改为:
procedure ByVal(B: Boolean; I: Integer = 0);
 
因为默认值必须是一个常数表达式,所以dynamic-array、procedural、class、class-reference和interface等参数只能指定nil默认值;而record、variant、file和static-array等类型的参数则根本不能指定默认值。
 
如果按值传递一个指针类型的参数,情况会变得复杂而又很有意思。此时,实际传递的是什么呢?是实际数据的拷贝吗?不,是指针的拷贝,也就是说形参和实参是两个指针,不过这两个指针指向了相同地址。所以这时候,形参和实参可以共享它们指向地址中的数据,但如果改变了形参的指针指向,实参的指针指向不能跟着改变。那么总结一下,就是:按值传递指针参数时,实参和形参可以共享指针指向地址中的数据,但是不能共享指针本身的指向。而引用传递时,因为实参和形参是同一个变量,因此实现完全共享。看下面的例子:
 
procedure TForm1.ByVal(Obj: TObject);
begin
  Obj := Button1;    
  {改变形参指针指向,实参的指针指向不会跟着改变,因为它们是两个变量。如果仅仅是改变Obj的属性而不改变指向,则实参的属性会跟着改变}
end;
 
procedure TForm1.ByRef(var Obj: TObject);
begin
  Obj := Button1;        
  {改变形参指针指向,实参的指针指向跟着改变,因为它们是同一个变量}
end;
 
procedure TForm1.Button1Click(Sender: TObject);
var
  Obj: TObject;
begin
  Obj := Self;           
  {Self即Form1,所以此时实参Obj的类名(ClassName)是"TForm1"}
  ByVal(Obj);                    {按值传递指针变量Obj}
 
  ShowMessage(Obj.ClassName);    {显示类名"TForm1"}
  ByRef(Obj);                       {引用传递指针变量Obj}
  ShowMessage(Obj.ClassName);    {显示类名"TButton1"}
end;
 
上面讲了这么多,最根本的还是一句话:按值传递时,形参和实参是两个变量;引用传递时,形参和实参是同一个变量。抓住这句话,就等于抓住了一切。(ps:关键总结):
相信你还看到过如下格式的参数声明:
function CompareStr(const S1, S2: string): Integer;
function TryStrToInt(const S: string; out Value: Integer): Boolean;
其中使用了const和out关键字。如果你没有看到过这样的声明,也不要紧,它们是真实存在的。
const声明的参数是按值传递的,而且形参不能被改变。
out声明的参数是引用传递的,主要用于定义输出参数,也就是说不需要输入值(即实参不需要初始化),实参传递给形参的值被忽略。
如果用const修饰指针参数,那么只能通过形参修改指针地址里的数据而不能修改指针本身的指向。例如对于一个const对象参数,可以修改其属性,但是不能将它指向其他对象。例如:
 
procedure ShowInfo(const Form: TForm);
begin
  {以下一句不能通过,编译器提示:[Error] Unit1.pas(28): Left side  cannot be assigned to}
  {Form := Form1;}
  {但是通过其属性或者方法修改隶属于Form的数据}
  Form.Caption := 'lxpbuaa';
  ShowMessage(Form.Caption);
end;
 
在本小节的最后,还不得不提及一种很特殊的参数类型:无类型参数(Untyped parameters)。
声明时没有指定数据类型的参数称为无类型参数。因此,从语法上讲,无类型参数可以接收任何类型的数据。
无类型参数必须加const、out或var前缀;无类型参数不能指定默认值。
 
如以下一些Delphi定义的过程都使用了无类型参数:
 
procedure SetLength(var S; NewLength: Integer);       {参数S}
procedure Move(const Source;var Dest;Count:Integer);   {参数Source、Dest}
procedure TStream.WriteBuffer(const Buffer; Count: Longint);{参数Buffer}
 
所谓无类型参数可以接收任何类型的值,只是从语法角度而言的。或者说,理论上我们可以实现一个可以使用任何类型变量作为参数的过程,但是实际上没有必要,也不可能做到。
打个比方说,我们想造一辆可以装载任何物体的汽车。因为是“任何物体”,所以物体可能是任何形状,于是这辆车必须没有车篷,除了在几个车轮上铺一个足够大(足够大就已经是个大问题了)的平板外,不能再有任何东西。这时候,这个平板就可以看做是无类型的,因为它上面可以坐人、摆一张桌子,也可以赶一些动物上去站着或者躺着。尽管它可以承载很多种类的东西,但是也是有限制的,比如不能放一座山、也无法容纳1万头猪。所以无类型参数的类型往往是有一定限制的。比如SetLength的参数S只能是字符串、动态数组等。
这种限制一般是在过程的实现中完成的,在运行时检查参数值的实际类型。对于与开发环境关系紧密的参数,限制也可以构筑在编译器里。
使用无类型参数的原因是无法在声明时使用一个统一的类型来描述运行时可能的类型,如SetLength的参数S可以是字符串和动态数组,而并没有一个统一的类型来代表字符串和动态数组类型,所以干脆声明为无类型。而将类型限制放到别的地方实现(如编译器)。例如SetLength的限制规则是写在编译器中的,它只能作用于长字符串或者动态数组。你企图完成下面的功能时:
var
  I: Integer;
begin
  SetLength(I, 10);
end;
 
编译器编译时将给出错误信息:[Error] Unit1.pas(35): Incompatible types。导致编译中断。
 
小结
本小节的内容比较重要,重点是理解参数按值传递和引用传递的本质:按值传递时,形参和实参是两个变量;引用传递时,形参和实参是同一个变量。
 
声明指令
声明一个过程,可以使用register、pascal、cdecl、stdcall和safecall指令来指定参数传递顺序和参数内存管理方式,从而影响过程的运作。如:
function MyFunction(X, Y: Integer): Integer; cdecl;
 
这五个指令具有不同含义,如表3-1所示。
 
表3-1   五个指令的不同含义
 
 指令     参数存放位置      参数传递顺序   参数内存管理        适用地点
 register  CPU寄存器      从左到右      被调用者   默认.published属性存取                                                                 方法 必须使用
 pascal         栈           从左到右      被调用者    向后兼容,不再使用
 cdecl          栈           从右到左      调用者      调用C/C++共享库
 stdcall         栈           从右到左      被调用者    API调用,如回调函数
 safecall        栈           从右到左      被调用者    API调用,如回调函数。双
 
在一些源代码(包括Delphi自带的VCL源代码)中,你还可能看到near、far、export以及inline、assemble等指令,它们是为了和16位Windows系统或者早期Pascal/Delphi兼容,在目前的Delphi版本中,已经不具有任何意义,所以在新的开发中不要再使用。
 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • Delphi中的消息处理机制 delphi 方法
    每一个VCL都有一内在的消息处理机制,其基本点就是构件类接收到某些消息并把它们发送给适当的处理方法,如果没有特定的处理方法,则调用缺省的消息处理句柄。    其中mainwndproc是定义在Twincontrol类中的一个静态方法,不能被重载(Override)。它不直接处
    02-09
  • Delphi XE6 通过JavaScript API调用百度地图
    Delphi XE6 通过JavaScript API调用百度地图
    参考昨天的内容,有朋友还是问如何调用百度地图,也是,谁让咱都在国内呢,没办法,你懂的。 首先去申请个Key,然后看一下百度JavaScript的第一个例子:http://developer.baidu.com/map/jsdemo.htm下一步,就是把例子中的代码,移动TWebBrower中。 unit Unit
    02-09
  • Delphi编译/链接过程 delphi编程案例
    Delphi编译/链接过程 delphi编程案例
    下面展示了Delphi是怎样编译源文件,并且把它们链接起来,最终形成可执行文件。当Delphi编译项目(Project)时,将编译项目源文件、窗体单元和其他相关单元,在这个过程中将会发生好几件事情:首先,Object Pascal编译器把项目单元编译为二进制对象文件,然后
    02-09
  • Delphi CompilerVersion Constant / Compiler C
    http://delphi.wikia.com/wiki/CompilerVersion_Constant The CompilerVersion constant identifies the internal version number of the Delphi compiler.It is defined in the System unit and may be referenced either in code just as any other consta
    02-09
  • Delphi revelations #1 – kbmMW Smart client
    Delphi 启示 #1 – kbmMW Smart client on NextGen (Android) – 作用域问题以更高级的方式使用kbmMW smart client,在Android设备上,我遇到了问题。通过继承TInvokeableVariantType,kbmMW smart client可以使用Delphi支持的特殊类型的自定义Variant,从而可
    02-09
  • Delphi 调用DLL外部函数时的指针参数
    某项目需要调用设备厂家提供的DLL的函数,厂家给了一个VB的例子,有个参数是ByRef pBuffer As Single。于是在Delphi中用buffer:array of single代替:function func(buffer:array of single;count:integer):integer;far;stdcall;external 'func.dll';调用后bu
    02-09
  • 《zw版·Halcon-delphi系列原创教程》 Halcon分
    《zw版·Halcon-delphi系列原创教程》 Halcon分类函数012,polygon,多边形为方便阅读,在不影响说明的前提下,笔者对函数进行了简化::: 用符号“**”,替换:“procedure”:: 用大写字母“X”,替换:“IHUntypedObjectX”:: 省略了字符:“const”、“OleVa
    02-09
  • 最简单的delphi启动画面(转)
    首先做一窗体,然后将BorderStyle的属性设为bsnone,放image控件,align设为alclient 然后将主程序的修改为 uses Windows, Forms, Unit1 in 'Unit1.pas' {Form1}, Unit2 in 'Unit2.pas' {Form2}; {$ R *.res} begin Application.Initialize; Form2:=TForm2.Cre
    02-09
  • Delphi备忘三:TCollection的使用,用Stream保
     代码unit ufrmGetFunctionDefine;interfaceuses  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,TypInfo,  Dialogs,ufrmStockBaseCalc, StdCtrls, ComCtrls,uQEFuncManager,uWnDataSet,uDataService;type  T
    02-09
  • Delphi Dcp 和BPL的解释
    dcp = delphi compiled package,是 package 编译时跟 bpl 一起产生出来的,记录着 package 中公开的 class、procedure、function、variable、const.... 等等的名称和相对位址。package英文翻译过来就是“包”。如果 某个控件包 A 引用了 控件包 B,当 控件包
    02-09
点击排行