【问题标题】:Delphi procedures with string parameters带字符串参数的 Delphi 程序
【发布时间】:2016-03-10 20:30:23
【问题描述】:

我在 Delphi 中处理过程和字符串时遇到了问题。事实是我希望看到输出字符串“1S2S3S4S5S6S”,但实际输出是“1234S5S6”。在调试过程中,它说 S1、S2、S3 和 S6 字符串变量未初始化(S1、S2、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, S, S, S, S);
  writeln(S);
end;

begin
  Work;
  readln;
end.

【问题讨论】:

  • 当心:当你用const 声明一个参数时,你是在告诉编译器它不应该期望参数在该函数的持续时间内发生变化。你有责任确保你信守承诺;编译器无法为您检查它。在这种情况下,您正在修改 SS7,同时声明 S2S5 不会改变。

标签: delphi delphi-7


【解决方案1】:

您的S7 参数被声明为out 参数,因此在调用函数时编译器会将传递的变量设置为空字符串。您正在为所有参数(包括输出参数)传递相同的 S 变量,因此在函数内部使用参数值之前,S 的值会从内存中擦除。

为了进一步详细说明,该过程使用register 调用约定,其中S1..S3 在CPU 寄存器(分别为EAX、EDX 和ECX)和S4..@987654329 中传递@ 而是在堆栈上传递。输入string 变量在其当前值被推入堆栈S4S5S3S6 只是指向变量的指针)之后,在值被分配到之前被清除干净S1S2。因此,S1S2 最终为零,S4S5 包含指向擦除前原始 'S' 数据的指针,S3S6 指向 string 变量被抹去。

调试器可以向您展示所有这些操作。如果你在调用MyProcedure()的那一行下断点,然后打开CPU视图,你会看到如下汇编指令:

StringTest.dpr.17: MyProcedure(S, S, S, S, S, 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, S, S, S, S, 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, S, S, S, S));
end;

【讨论】:

  • 感谢您的建议,但我仍然不明白为什么 S4 和 S5 的值为“S”而其他的则没有。怎么了?
  • 好的,我现在明白了 :) 最后一个问题,为什么会按这样的顺序发生?我的意思是先将 S4、S5 的值推入堆栈,然后将 S1、S2、S3 推入寄存器。
  • 如果寄存器参数首先存储到寄存器中,@Alexander,那么编译器将无法使用这些寄存器来计算堆栈参数的值。编译器知道它可以按照它选择的任何顺序来计算参数值,所以它会选择一个方便编译器的顺序。
猜你喜欢
  • 2021-06-19
  • 1970-01-01
  • 2021-10-07
  • 2019-11-23
  • 1970-01-01
  • 1970-01-01
  • 2021-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多