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;
是因为某些速度/内存损失?更有经验的人可以解释一下原因/差异吗?
答案 0 :(得分:3)
除了性能之外,在编写良好的代码中,两个函数之间没有区别。
在编写错误的代码中,存在巨大差异。使用局部变量使很多更容易推断函数的正确性。内存中有S
和Pos
重叠的可能性。如果Inc(Pos);
修改字符串的内容或长度,您是否想要预测程序的行为?
使用局部变量可以很容易地看到正好会发生什么。我当然会同意S
和Pos
重叠是一个可怕的错误使用过程,但程序的行为应符合其规范,因此规范应该禁止这样的输入值,或者即使存在这样的输入值,过程也应该按照指定的方式运行。获得有效和无效的精确细节是棘手的。使程序在所有情况下都能正常工作很容易。
答案 1 :(得分:2)
另外,避免将字符串索引作为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)需要内存访问,但这与字符串索引计算无关。