关于 Delphi 参数传递方式的一点研究

原创 2004年08月27日 23:41:00
某次看 D6DG 说默认的参数传递方式因为会为变量产生本地副本所以会消耗额外的内存,而 const 方式会优化字符串和记录类型的参数传递时的内存占用。从而猜测 const 方式的参数传递实际也是按地址传递,只是编译器强制不允许函数内的代码修改 const 方式传递进去的变量而已。
为了证实我的猜想,特设计以下实验,本实验中使用到了字符串(本文提到的字符串都是指 Delphi 默认的字符串 AnsiString)内部结构中的引用计数。
关于字符串的引用计数,结合 D6DG 中的说明和偶的实际研究,得出的结论是,字符串地址实际上是其内容的第一个字符的地址,在此地址之前的 12 字节内存中的内容才是字符串内部结构中的头部,分别是 32 位字符串占用的内容空间大小,32 位引用计数,32 位字符串长度(仅在 Delphi 7 中验证,推测 Delphi 7 以前的 32 位 Delphi 中的字符串结构都应该是这样,未验证)。


以下代码中 sGlobal 为全局字符串变量。





在主程序里分别以初始化后的 sGlobal 为实参调用这几个过程后,我们会看到三种方式 S 的地址都跟 sGlobal 的一样,这初步说明 var 方式和 const 方式确实都是按地址传递参数。
但不可思议的是,默认参数传递方式不会是为变量产生本地副本吗?为什么 S 的地址还会跟 sGlobal 一样呢?因为一开始我设计这个实验时并没有添加显示引用计数的代码,所以很是迷茫。之后想起了 Borland 使用了引用计数的方式和 copy-on-write 技术(对于字符串等使用这两个技术的数据类型的变量 A、B,将 A 赋值给 B 时实际上只是赋值 A 的地址给 B,并增加 A 的引用计数,直到两者其中一个被修改时才为 B 申请新的内存空间并且复制 A 的内容,同时减少 A 的引用计数)来优化字符串的操作。于是查阅 D6DG 并且观察 Delphi 的汇编代码和内存(在 CPU 窗口中)得出了本文开头关于字符串引用计数的结论。
这样,添加了显示引用计数的代码之后我们就可以观察到,使用默认方式时 S 和 sGlobal 的引用计数,都是 2,而其他方式时两者的引用计数都是 1,这更有力的说明了 const 方式确实是按地址传递,而不是像默认方式那样只是增加引用计数。这样,当我们把第一个过程中第一行代码的注释去掉后再次运行程序,可以看到默认方式时 S 的地址已经跟 sGlobal 不同了,同时两者的引用计数都是 1 了,说明确实是在 S 被修改后才产生本地副本(copy-on-write)。

那么,现在我们知道参数类型为字符串时, const 方式的参数传递实际是传递参数地址,这可以优化内存的使用,而参数为其他数据类型时是不是这样呢?
为此修改刚才的程序如下,其中 iGlobal 为全局整形变量。


procedure Method1(I: Integer);
begin
Form1.Memo1.Lines.Add('I 地址: ' + IntToStr(Integer(@I)));
Form1.Memo1.Lines.Add('');
end;

procedure Method2(var I: Integer);
begin
Form1.Memo1.Lines.Add('I 地址: ' + IntToStr(Integer(@I)));
Form1.Memo1.Lines.Add('');
end;

procedure Method3(const I: Integer);
begin
Form1.Memo1.Lines.Add('I 地址: ' + IntToStr(Integer(@I)));
Form1.Memo1.Lines.Add('');
end;

procedure iGlobalInfo;
begin
Form1.Memo1.Lines.Add('iGlobal 地址: ' + IntToStr(Integer(@iGlobal)));
Form1.Memo1.Lines.Add('');
end;

在主程序里分别以初始化后的 iGlobal 为实参调用这个过程后,可以看到只有 var 方式中 I 的地址跟 iGlobal 一样,而默认方式和 const 都会为 iGlobal 产生本地副本,可见确实如 D6DG 所说 const 会(也只会)优化字符串和记录类型的参数传递时的内存占用。

至此实验目的达到,还附带了解了 string 的内部格式。

附上实验程序源代码。
点击下载

Delphi程序带参数运行

程序1 program E1; uses Forms,Dialogs,SysUtils, EndM1 in 'EndM1.pas' {Form2}; {$R *.res} begin ...
  • hzken0137
  • hzken0137
  • 2017-08-01 13:58:21
  • 674

让Delphi使用带参数来调用外部可执行文件(EXE文件)

{*********************************************************}{                                        ...
  • jiang5460
  • jiang5460
  • 2005-11-04 09:15:00
  • 6708

Delphi中的参数传递方式

.参数传递方式:         Delphi中有自己的参数传递方式,而Windows API也有自己的参数传递方式,那么他们之间有什么不同呢,要如何做到兼容呢,尤其是在编写动态库时?  (1)cde...
  • shuaihj
  • shuaihj
  • 2004-10-30 09:17:00
  • 2246

将函数或过程作为参数进行传递实例

又名:将事件作为参数进行传递实例 首先要明白DELPHI中的事件是指针来的。 1 声明一个事件: type     TChangeEvent = procedure (ASelectType,...
  • duck04551
  • duck04551
  • 2011-11-15 09:58:25
  • 1183

delphi中传参数才能运行的软件编写(ParamStr)

delphi中传参数才能运行的软件编写(ParamStr) ParamStr 表示的是启动应用程序时传给其的参数,比如在windows的dos系统下你想用命令符操作notepad.exe打开1....
  • singular2611
  • singular2611
  • 2015-03-19 14:48:48
  • 1130

用Delphi实现程序间的数据传递

用Delphi实现程序间的数据传递 在实际应用中,我们经常需要多个程序相互配合来完成某些特定功能。例如两个应用程序间的同步、互斥;应用程序在起第二份实例时的参数自动传递…。要实现这些功能,就必须能实现...
  • iiprogram
  • iiprogram
  • 2005-03-03 11:32:00
  • 1895

Java参数传递PPT

  • 2008年04月20日 23:10
  • 873KB
  • 下载

delphi参数传递

delphi参数传递 参数传递     声明/实现一个过程使用的参数称为形式参数(简称形参),调用过程时传入的参数称为实际参数(简称实参)。 { Info是形参} procedur...
  • ly930156123
  • ly930156123
  • 2016-09-07 14:59:49
  • 1069

让你的delphi程序支持外部参数

procedure TForm1.FormCreate(Sender: TObject);vari: Integer;for i := 1 to ParamCount dobeginif LowerC...
  • Vsun
  • Vsun
  • 2005-04-05 11:21:00
  • 979

Delphi 参数传递

1.数值参数  变量和结构被完全拷贝到堆栈中,调用函数接受到的只是一个副本.数值参数传递方式是Delphi的默认方式,也是其他大部分语言的默认参数传递方式(比如C++,VC,java...).形式: ...
  • Vigorcsdn
  • Vigorcsdn
  • 2005-01-21 10:14:00
  • 3901
收藏助手
不良信息举报
您举报文章:关于 Delphi 参数传递方式的一点研究
举报原因:
原因补充:

(最多只允许输入30个字)