I faced a problem while working with procedures and strings in Delphi. The fact is that I expected to see the output string "1S2S3S4S5S6S" but the actual output is "1234S5S6". During the debug process it says that S1, S2, S3 and S6 string variables are not initialized (S1, S2, S3, S6 are '' strings, S4 and S5 have value 'S'). Can someone explain to me this? Here's the code:
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.
Your S7
parameter is declared as an out
parameter, so the compiler will set the passed variable to a blank string when the function is called. You are passing the same S
variable for all of the parameters, including the output parameter, so the value of S
gets wiped from memory before the parameter values are used inside the function.
To elaborate further, the procedure is using the register
calling convention, where S1
..S3
are passed in CPU registers (EAX, EDX, and ECX, respectively) and S4
..S6
are passed on the stack instead. The input string
variable is getting wiped clear after its current value is pushed on the stack for S4
and S5
(S3
and S6
are just pointers to the variable), and before the value is assigned to S1
and S2
. So, S1
and S2
end up nil, S4
and S5
contain pointers to the original 'S'
data before the wipe, and S3
and S6
are pointing at the string
variable that was wiped.
The debugger can show you all of this in action. If you put a breakpoint at the line where MyProcedure()
is called, and then open the CPU view, you will see the following assembly instructions:
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
To fix this, you need to use a different variable to receive the output:
procedure Work;
var
S, Res: String;
begin
S := 'S';
Proc(S, S, S, S, S, S, Res);
WriteLn(Res);
end;
Alternatively, change the procedure into a function that returns a new String
via its Result
instead of using an out
parameter:
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With