我有这个代码,有一个使用重载和默认参数的过程:
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.
Run Code Online (Sandbox Code Playgroud)
输出是:
1
3
Run Code Online (Sandbox Code Playgroud)
请注意,第一次调用 Foo
不提供默认值。为什么会这样?这个问题只与非常旧的编译器有关吗?
这仅在使用overload
密钥时发生。
procedure Foo2(const a: array of string; b: Boolean=False);
begin
Writeln(Length(a));
end;
Foo2(['1', '2', '3']);
Run Code Online (Sandbox Code Playgroud)
工作正常。
正如您所发现的并且 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;
Run Code Online (Sandbox Code Playgroud)
您可以通过Foo
在 CPU 窗口 ( Ctrl++ ) 中调试并检查汇编代码来准确观察编译器如何无法正确调用。AltC
您应该能够推断出该Foo
过程的编译符合预期:
eax
ecx
High
数组的索引在edx
注意我使用Integer
default 来获得更独特的默认值。
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
1) 正如上面案例 2 所指出的,当 bug 出现时,ecx
未初始化。下面应该演示效果:
procedure Foo(const a: array of string; b: Integer = 2); overload;
var
I: Integer;
begin
for I := Low(a) to High(a) do Write(a[I]);
Writeln(b);
end;
...
Foo(['a', 'b', 'c'], 23); {Will write abc23}
Foo(['a', 'b', 'c']); {Will write abc, but the number probably won't be 2}
Run Code Online (Sandbox Code Playgroud)
2) 该错误不会在动态数组中出现。动态数组的长度是其内部结构的一部分,因此不能被忘记。