为什么在过程中使用局部变量而不是var参数?

时间:2016-02-14 21:37:29

标签: delphi

o在Delphi中,SysUtils是ScanBlanks程序:

procedure ScanBlanks(const S: string; var Pos: Integer);
var
  I: Integer;
begin
  I := Pos;
  while (I <= Length(S)) and (S[I] = ' ') do Inc(I);
  Pos := I;
end;

我想知道为什么程序使用I变量。我们不能直接使用Pos var吗?

procedure ScanBlanks(const S: string; var Pos: Integer);
begin
  while (Pos <= Length(S)) and (S[Pos] = ' ') do Inc(Pos);
end;

是因为某些速度/内存损失?更有经验的人可以解释一下原因/差异吗?

2 个答案:

答案 0 :(得分:3)

除了性能之外,在编写良好的代码中,两个函数之间没有区别。

在编写错误的代码中,存在巨大差异。使用局部变量使很多更容易推断函数的正确性。内存中有SPos重叠的可能性。如果Inc(Pos);修改字符串的内容或长度,您是否想要预测程序的行为?

使用局部变量可以很容易地看到正好会发生什么。我当然会同意SPos重叠是一个可怕的错误使用过程,但程序的行为应符合其规范,因此规范应该禁止这样的输入值,或者即使存在这样的输入值,过程也应该按照指定的方式运行。获得有效和无效的精确细节是棘手的。使程序在所有情况下都能正常工作很容易。

答案 1 :(得分:2)

documentation说:

  

另外,避免将字符串索引作为var参数传递,因为这会导致代码效率低下。

但我不认为这总是如此。给出一个简单的示例,优化器将尽力而为。在我看来,这是一个微观优化,在编写代码时不应该打扰你。见Jeff Atwood

  

没关系!没关系!

让我们看一个例子:

procedure StringIndexByVar(const S: string; var I: integer);
begin
  I := 1;
  while I <= Length(S) do
  begin
    Write(S[I]);
    inc(I);
  end
end;

procedure StringIndexByLocal(const S: string; var I: integer);
var
  LIndex: integer;
begin
  LIndex := 1;
  while LIndex <= Length(S) do
  begin
    Write(S[LIndex]);
    inc(LIndex);
  end;
  I := LIndex;
end;

这会在Win32编译为:

StringIndexByVar:

00417ACC 53               push ebx
00417ACD 56               push esi
00417ACE 8BDA             mov ebx,edx
00417AD0 8BF0             mov esi,eax
00417AD2 C70301000000     mov [ebx],$00000001
00417AD8 EB1D             jmp $00417af7
00417ADA 8B03             mov eax,[ebx]
00417ADC 0FB75446FE       movzx edx,[esi+eax*2-$02] <--- (1)
00417AE1 A18CC54100       mov eax,[$0041c58c]
00417AE6 E8E9D2FEFF       call @Write0WChar
00417AEB E824CCFEFF       call @Flush
00417AF0 E88FC6FEFF       call @_IOTest
00417AF5 FF03             inc dword ptr [ebx] <--- (2)
00417AF7 8BC6             mov eax,esi
00417AF9 E8C2F2FEFF       call @UStrLen
00417AFE 3B03             cmp eax,[ebx]
00417B00 7DD8             jnl $00417ada
00417B02 5E               pop esi
00417B03 5B               pop ebx
00417B04 C3               ret

StringIndexByLocal:

00417B08 53               push ebx
00417B09 56               push esi
00417B0A 57               push edi
00417B0B 8BFA             mov edi,edx
00417B0D 8BF0             mov esi,eax
00417B0F BB01000000       mov ebx,$00000001
00417B14 EB1A             jmp $00417b30
00417B16 A18CC54100       mov eax,[$0041c58c]
00417B1B 0FB7545EFE       movzx edx,[esi+ebx*2-$02]  <---(1)
00417B20 E8AFD2FEFF       call @Write0WChar
00417B25 E8EACBFEFF       call @Flush
00417B2A E855C6FEFF       call @_IOTest
00417B2F 43               inc ebx
00417B30 8BC6             mov eax,esi
00417B32 E889F2FEFF       call @UStrLen
00417B37 3BD8             cmp ebx,eax
00417B39 7EDB             jle $00417b16
00417B3B 891F             mov [edi],ebx
00417B3D 5F               pop edi
00417B3E 5E               pop esi
00417B3F 5B               pop ebx
00417B40 C3               ret

据我所知,字符串索引在这里没有区别,因为索引被加载到ebx(用箭头1标记)。增加循环计数器(箭头2)需要内存访问,但这与字符串索引计算无关。