在Delphi中处理程序和字符串时遇到问题.事实上,我预计会看到输出字符串“1S2S3S4S5S6S”,但实际输出为“1234S5S6”.在调试过程中,它说S1,S2,S3和S6字符串变量没有被初始化(S1,S3,S6是“串”,S4和S5有值“S”).有人可以向我解释一下吗以下是代码:
program StringTest; {$APPTYPE CONSOLE} procedure MyProcedure(S1: String; const S2: String; var S3: String; S4: String; const S5: String; var S6: String; out S7: String); begin S7 := '1' + S1 + '2' + S2 + '3' + S3 + '4' + S4 + '5' + S5 + '6' + S6; end; procedure Work; var S: String; begin S := 'S'; MyProcedure(S,S,S); writeln(S); end; begin Work; readln; end.
解决方法
您的S7参数被声明为out参数,因此编译器将在调用该函数时将传递的变量设置为空字符串.您正在为所有参数传递相同的S变量,包括输出参数,所以在函数内部使用参数值之前,S的值将从内存中擦除.
要进一步阐述,程序正在使用寄存器调用约定,其中S1..S3分别在cpu寄存器(EAX,EDX和ECX)中传递,而S4..S6则传递给堆栈.输入字符串变量在其当前值被推送到堆栈S4和S5(S3和S6只是指向变量的指针)之后,并且该值被分配给S1和S2之前被清除.所以,S1和S2结束为零,S4和S5在擦除之前包含指向原始“S”数据的指针,S3和S6指向被擦除的字符串变量.
调试器可以显示所有这些操作.如果在调用MyProcedure()的行上放置断点,然后打开cpu视图,您将看到以下装配说明:
StringTest.dpr.17: MyProcedure(S,S); 00405A6C 8B45FC mov eax,[ebp-$04] // [ebp-$04] is the current value of S 00405A6F 50 push eax // <-- assign S4 00405A70 8B45FC mov eax,[ebp-$04] 00405A73 50 push eax // <-- assign S5 00405A74 8D45FC lea eax,[ebp-$04] 00405A77 50 push eax // <-- assign S6 00405A78 8D45FC lea eax,[ebp-$04] 00405A7B E8B0EDFFFF call @UStrClr // <-- 'out' wipes out S! 00405A80 50 push eax // <-- assign S7 00405A81 8D4DFC lea ecx,[ebp-$04] // <-- assign S3 00405A84 8B55FC mov edx,[ebp-$04] // <-- assign S2 00405A87 8B45FC mov eax,[ebp-$04] // <-- assign S1 00405A8A E8B9FEFFFF call MyProcedure
procedure Work; var S,Res: String; begin S := 'S'; Proc(S,Res); WriteLn(Res); end;
或者,将过程更改为通过其Result而不是使用out参数返回新String的函数:
function MyFunction(S1: String; const S2: String; var S3: String; S4: String; const S5: String; var S6: String): String; begin Result := '1' + S1 + '2' + S2 + '3' + S3 + '4' + S4 + '5' + S5 + '6' + S6; end; procedure Work; var S: String; begin S := 'S'; WriteLn(MyFunction(S,S)); end;