使用带有默认参数和重载过程的字符串数组

时间:2017-12-26 14:41:07

标签: delphi delphi-5

我有这个代码,其过程使用了重载和默认参数:

program Project2;
{$APPTYPE CONSOLE}
uses SysUtils;

procedure Foo; overload; // not actually needed to reproduce
begin
end;

procedure Foo(const a: array of string; b: Boolean=False); overload;
begin
  Writeln(Length(a));
end;

begin
  Foo(['1', '2', '3']); // => 1 ???
  Foo(['1', '2', '3'], False); // => 3 OK
  Readln;
end.

输出结果为:

1
3

请注意,对Foo的第一次调用不提供默认值。为什么会这样?这个问题只与很老的编译器有关吗?

只有在使用overload密钥时才会发生这种情况。

procedure Foo2(const a: array of string; b: Boolean=False);
begin
  Writeln(Length(a));
end;

Foo2(['1', '2', '3']);

工作正常。

2 个答案:

答案 0 :(得分:4)

这显然是 Delphi-5 中的错误。只有在满足以下条件时才会发生:

  • 该程序标有overload

  • array of string是第一个参数

  • 没有将默认参数传递给过程

答案 1 :(得分:2)

摘要

正如您所发现的那样,David帮助澄清:这是Delphi 5中的一个错误(可能还有那个时代的其他几个版本)。在特定条件下,编译器无法正确调用该过程。

这实际上是两个特征的冲突:

  • 一个开放数组允许调用者将一个未指定长度的固定数组传递给一个过程。编译器在编译时确定长度并传递其他隐藏参数(High索引),以便该方法可以正确地确定数组中元素的数量。
  • 默认参数只是语法糖,允许调用者省略默认值。实现不受影响,但编译器会自动传递省略的参数,就像调用者已通过默认值一样。
  • 当程序标记为过载时,会发生错误。编译器似乎“忘记”传递隐藏High索引,并在其位置传递默认值。

解决方法

我确定您已经在使用明显的解决方法,但我将其包含在内是为了完整性。当我以前在Delphi 5中工作时,我们替换了 array of String 的所有组合,并默认使用以下内容; (无论我们是否已经使用 overload )。

procedure Foo(const a: array of string; b: Boolean); overload; {Remove the default}
begin
  ...
end;
procedure Foo(const a: array of string); overload;
begin
  Foo(a, False); {And pass the default value via overload}
end;

详细

您可以通过在CPU窗口中调试( Ctrl + Alt + C )并检查汇编代码。

您应该能够推断出Foo过程已编译为期望:

  • Foo
  • 中打开数组的地址
  • eax
  • 中的第二个参数(默认值)
  • ecx
  • 中数组的High索引

注意我使用edx默认值来获得更具特色的默认值。

案例1

Integer

案例2

procedure Foo(const a: array of string; b: Integer = 7);
...
Foo(['a', 'b', 'c']);
{The last few lines of assembler for the above call}
lea eax,[ebp-$18] {Load effective address of array}
mov ecx,$00000007 {Implicitly set default value 7}
mov edx,$00000002 {The hidden High value of the open array}
call Foo

案例3

procedure Foo(const a: array of string; b: Integer = 7); overload;
...
Foo(['a', 'b', 'c']);

lea eax,[ebp-$18]
{The second parameter is now uninitialised!}
mov edx,$00000007 {Instead the default is assigned to register for High(a)}
call Foo

其他观察

1)正如上面的案例2中所指出的,当错误显示时,procedure Foo(const a: array of string; b: Integer = 7); overload; ... Foo(['a', 'b', 'c'], 5); lea eax,[ebp-$18] mov ecx,$00000005 {The explicit argument for 2nd parameter} mov edx,$00000002 {The hidden parameter is again correctly assigned} call Foo 未被初始化。以下内容应证明效果:

ecx

2)动态数组不会出现错误。动态数组的长度是其内部结构的一部分,因此不能忘记。