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版本中,已经不具有任何意义,所以在新的开发中不要再使用。

delphi参数传递的更多相关文章

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

    //delphi中exit,abort,break,continue 的区别 exit: 退出函数体abort: 遇到异常,安静处理,就是不显示不提示break: 退出当前循环体,包括for ,whi ...

  2. 参数传递方法(用Delphi的汇编代码解释)

    参数传递方法 李纬的InsideVCL<第一章>中提到Windows定义的回调函数 typedef LRESULT (CALLBACK*WNDPROC)(HWND,UNIT,WPARAM, ...

  3. Delphi函数参数传递 默认参数(传值)、var(穿址)、out(输出)、const(常数)四类

    Delphi的参数可以分为:默认参数(传值).var(传址).out(输出).const(常数)四类 可以对比C/C++的相关知识,类比学习. 1.默认参数是传值,不会被改变,例子 function ...

  4. 004.Delphi插件之QPlugins,参数传递

    界面如下 插件框架中大量使用了接口的东西,看的眼花缭乱,很多地方只做了申明,具体的实现是在另外的子类. DLL的代码如下 unit ParamTest; interface uses classes, ...

  5. delphi 函数参数传递 默认参数(传值)、var(传址)、out(输出)、const(常数)四类

    参数可以分为: 默认参数(传值).var(传址).out(输出).const(常数)四类 {默认参数是传值, 不会被改变} function MyF1(x: Integer): Integer; be ...

  6. Delphi之静态方法,虚方法virtual,动态dynamic,抽象abstract,消息

    Delphi之静态方法,虚方法virtual,动态dynamic,抽象abstract,消息 http://www.cnblogs.com/zhwx/archive/2012/08/28/266055 ...

  7. Delphi:与VCL同步(Synchronize()、用消息来同步)

    看本文时,可以同时参考:Delphi中线程类 TThread实现多线程编程(事件.临界区.Synchronize.WaitFor……) 先说一下RTL和VCL RTL(Run-Time library ...

  8. Delphi DLL的创建、静态及动态调用

    转载:http://blog.csdn.net/welcome000yy/article/details/7905463 结合这篇博客:http://www.cnblogs.com/xumenger/ ...

  9. Delphi 查找标题已知的窗口句柄,遍历窗口控件句柄(转)

    用我的方法来控制其他程序窗体上的窗口控件,必须先了解什么是 回调函数.我的理解是这样的: 回 调函数写出来不是自己的程序去调用的,反而是让其他的东西去调用,比如windows操作系统,比如其他的程序等 ...

随机推荐

  1. Google protobuf的安装及使用

    最近应为工作的需要,合作的部门提供了protobuf的接口,总结了一下使用的过程和方法如下: 下载protobuf-2.3.0: http://protobuf.googlecode.com/file ...

  2. 系统交易策略 hylt

    最令我尴尬的事情,莫过于很多朋友来到网站,不知道我说的是什么.大多数人以为鬼仆是推销软件的.其实这里理解是错的,特别是一些软件制作与经销商,更出 于推销的目的,故意夸大产品性能,模糊交易系统与一般行情 ...

  3. Java如何将Exception.printStackTrace()转换为String输出

    package com.test1; import java.io.PrintWriter; import java.io.StringWriter; public class T010 { /** ...

  4. Centos系统创建用户oracle后,用该用户登陆系统,页面加载报错GConf error

    Linux 的 GConf error 解决办法 问题: Centos系统创建用户oracle后,用该用户登陆系统,页面加载报错,导致重新进入Centos系统后出现: GConf error:Fail ...

  5. python 默认的系统编码 sys.setdefaultencoding

    python2.x的编码问题有时让人很头疼,一会ascii,一会unicode. 在脚本里多见这样的操作: import sys reload(sys) sys.setdefaultencoding( ...

  6. ASPNET5中的那些K

    ASPNET5中的那些K ASP.NET 5最大的变化是什么?首当其冲的就是多了很多K,K表示的是ASP.NET vNext的项目代号“Project K”,但很容易让人想到一个汉字——“坑”,希望K ...

  7. HDOJ/HDU 1113 Word Amalgamation(字典顺序~Map)

    Problem Description In millions of newspapers across the United States there is a word game called J ...

  8. linux下用Apache一个IP多个网站多域名配置方法

    如有两个域名,分别是desk.xker.com和tool.xker.com,需把这两个域名都绑定到IP是219.13.34.32的服务器上 1.首先需在域名供应商管理页面指定域名和IP的对应关系 2. ...

  9. Sicily1317-Sudoku-位运算暴搜

    最终代码地址:https://github.com/laiy/Datastructure-Algorithm/blob/master/sicily/1317.c 这题博主刷了1天,不是为了做出来,AC ...

  10. .Net设计模式_适配器模式

    引言: 有一次我看到一个朋友拿出一个三角型的东西,好奇的上去一看你,我勒个去,传说中的万能插口转接器(插口适配器). 一面是插入口,集合了我认识的所有插口的形状,也有国内没看见过的:另一个是跟插座接触 ...