Delphi中的函数(过程)是支持未知个数参数的(50分)

  • 主题发起人 主题发起人 liuchong
  • 开始时间 开始时间
L

liuchong

Unregistered / Unconfirmed
GUEST, unregistred user!
Delphi中的函数(过程)是支持未知个数参数的,当然不是用array of const那种
如:SetLength,对于一维,二维,三维,N维数组,就可以有N个参数
但不知是怎么实现的,看不到SetLength源码
 
用记录类型可以把整个记录传进去
 
传记录和参数个数没关系吧
 
编译器魔法。跟Write一样 后面参数个数可变。编译器会在你编写的“可见”代码里加入一些隐藏代码的。比如接口的自动释放。某些地方的try……finally
 
我们自己如何写一个知参数个数的函数
 
用变体数组了。编译器又不是你写的。不是未知参数就够了 还要在函数内部能够得到这些参数的值。
 
这个就像write函数一样.可以称为操作符.
因为它是受编译器本身支持的.
例如我们写如下代码:
var
s : array of
array of
array of
array of char;
begin
SetLength(s,
1,
1,
1,
1);
end;
生成的汇编代码如下:
Unit1.pas.31: begin
00455294 55 push ebp
00455295 8BEC mov ebp,esp
00455297 6A00 push $00
00455299 33C0 xor eax,eax
0045529B 55 push ebp
0045529C 68E1524500 push $004552e1
004552A1 64FF30 push dword ptr fs:[eax]
004552A4 648920 mov fs:[eax],esp
Unit1.pas.32: SetLength(s,
004552A7 6A01 push $01 //压入参数,立即数1
004552A9 6A01 push $01 //压入参数,立即数1
004552AB 6A01 push $01 //压入参数,立即数1
004552AD 6A01 push $01 //压入参数,立即数1
004552AF 8D45FC lea eax,[ebp-$04] //把动态数组地址装入eax
004552B2 B904000000 mov ecx,$00000004 //把参数ecx装入数组要分配的维数,就是告诉有4个数组维数参数.(4个1)
004552B7 8B1574524500 mov edx,[$00455274]//装入数组的类型信息.没有类型信息是没法分配的.连数组元素是什么类型都不知道还能分配?
004552BD E8C205FBFF call @DynArraySetLength //调用RTL函数DynArraySetLength
004552C2 83C410 add esp,$10
实际上我们查看Systems单元的函数_DynArraySetLength.发现一串注释.
{ PROCEDURE _DynArraySetLength(var a: dynarray
typeInfo: PDynArrayTypeInfo
dimCnt: Longint
lengthVec: ^Longint) }
说明和我们上面分析的一样._DynArraySetLength的参数为传动态数组引用,船动态数组类型信息,传要分配的维数,传维数数组(栈里面的维数指针,维数数据是挨着的,可以看成是数组).
参数和Setlength根本不一样.这个是编译器支持的.编译器编译到SetLength的时候会根据他的参数自动构造出添加参数的代码.然后调用_DynArraySetLength.
我们自己的代码想做到这个效果门都别想.除非CodeGear的编译器工程师是你小弟,哈哈
 
这东西搞的这么神秘,用开放数组参数即可,分固定类型开放数组和可变类型开放数组,下面摘录自pascal精要中文版:
“开放数组参数
与C语言不同,Pascal 函数及过程的参数个数是预定的。如果参数个数预先没有确定,则需要通过开放数组来实现参数传递。

一个开放数组参数就是一个固定类型开放数组的元素。 也就是说,参数类型已定义,但是数组中的元素个数是未知数。见下例:

function Sum (const A: array of Integer): Integer;
var
I: Integer;
begin
Result := 0;
for I := Low(A) to High(A) do
Result := Result + A;
end;
上面通过High(A)获取数组的大小,注意其中函数返回值 Result的应用, Result用来存储临时值。你可通过一个整数表达式组成的数组来调用该函数:

X := Sum ([10, Y, 27*I]);
给定一个整型数组,数组大小任意,你可以直接把它传递给带开放数组参数的例程,此外你也可以通过Slice 函数,只传递数组的一部分元素(传递元素个数由Slice 函数的第二个参数指定)。下面是传递整个数组参数的例子:

var
List: array [1..10] of Integer;
X, I: Integer;
begin
// initialize the array
for I := Low (List) to High (List) do
List := I * 2;
// call
X := Sum (List);
如果你只传递数组的一部分,可使用Slice 函数,如下:

X := Sum (Slice (List, 5));
例OpenArr中可见到包括上面的完整代码(见图6.1)。

在Delphi 4中,给定类型的开放数组与动态数组完全兼容(动态数组将在第8章中介绍)。动态数组的语法与开放数组相同,区别在于你可以用诸如array of Integer指令定义变量,而不仅仅是传递参数。

类型变化的开放数组参数
除了类型固定的开放数组外,Delphi 还允许定义类型变化的甚至无类型的开放数组。这种特殊类型的数组元素可随意变化,能很方便地用作传递参数。

技术上,array of const 类型的数组就能实现把不同类型、不同个数元素组成的数组一下子传递给例程。如下面Format 函数的定义(第七章中你将看到怎样使用这个函数):

function Format (const Format: string;
const Args: array of const): string;
上面第二个参数是个开放数组,该数组元素可随意变化。如你可以按以下方式调用这个函数:

N := 20;
S := 'Total:';
Label1.Caption := Format ('Total: %d', [N]);
Label2.Caption := Format ('Int: %d, Float: %f', [N, 12.4]);
Label3.Caption := Format ('%s %d', [S, N * 2]);
从上可见,传递的参数可以是常量值、变量值或一个表达式。声明这类函数很简单,但是怎样编写函数代码呢?……

由于内容过长,就不在贴出了,具体请参见pascal精要中文版第六章
此电子书可在网上找到,很多的。
我的网盘上也放了一个供delphi爱好者下载 http://ufo2003.ys168.com
 
后退
顶部