[fpc-devel] Wrong docs: not initialized global variables

Ondrej Pokorny lazarus at kluug.net
Sat Jul 7 10:25:49 CEST 2018


On 03.07.2018 19:53, Stefan Glienke wrote:
> SetLength should not cause anything uninitialized.

No, SetLength is not documented to clear the new initialized memory 
block so the new memory block can contain whatever you want.

It is documented not to touch the existing memory block in the array.

This is the case of Delphi and FPC. See GetS_Wrong2 in the second 
example below.

> It enlarges or shrinks the data and keeps any prior data as it was, new allocated memory is zeroed (at least that is how it works in the Delphi RTL and I would be very surprised if FPC would do any different).
>
> The core issue imo is temp var reuse the compiler does which causes hidden data reusage/copy which is only an issue with dynamic arrays because they don't have CoW semantic when writing to some index. So the temp variable still points to the first array which then gets passed to the next function as result. There a call to SetLength applies copy on write semantics carrying over any content from the previous array that is still in the temp variable to the newly allocated array.
>
> I think we discussed this on friday - maybe I did not make my point clear enough.

In your case I am entirely on the side with the FPC team. The result 
variable is not guaranteed to be cleared:

function DoSomething (len: Integer): Vector;
begin
   SetLength(Result, len); // << Result can be everything before the 
SetLength call
end;

So Result can have garbage data which stays there after the SetLength call.

This happens also in Delphi. Try this code:

program Project1;
{$APPTYPE CONSOLE}
var
   S_wrong, S_correct: string;
   I: Integer;
   function GetS_Wrong: string;
   begin
     Result := result+'X';
   end;
   function GetS_Correct: string;
   begin
     Result := 'X';
   end;
begin
   for I := 0 to 1 do
   begin
     S_wrong := S_wrong + GetS_Wrong;
     S_correct := S_correct + GetS_Correct;
   end;
   Writeln('Wrong: ', S_wrong);
   Writeln('Correct: ', S_correct);
   Readln;
end.

Output (in Delphi XE2):
Wrong: XXX
Correct: XX

----

In case of arrays try this code in Delphi 10:

program Project1;
{$APPTYPE CONSOLE}
type
   Vector = array of AnsiChar;
var
   W: AnsiChar = '1';
   W2: AnsiChar = '1';
   C: AnsiChar = '1';
   function GetS_Wrong: Vector;
   begin
     if Length(Result)<>1 then
       SetLength(Result, 1);
     Result[0] := W;
     Inc(W);
   end;
   function GetS_Wrong2: Vector;
   var
     ChangeNeeded: Boolean;
   begin
     ChangeNeeded := Length(Result)<>1;
     SetLength(Result, 1);
     if ChangeNeeded then
       Result[0] := W2;
     Inc(W2);
   end;
   function GetS_Correct: Vector;
   begin
     SetLength(Result, 1); // set length does a copy -> correct
     Result[0] := C;
     Inc(C);
   end;
   procedure WriteVec(Vec: Vector);
   var
     I: Integer;
   begin
     for I := Low(Vec) to High(Vec) do
       Write(Vec[I]);
   end;
var
   S_wrong, S_wrong2, S_correct: Vector;
   I: Integer;
begin
   for I := 0 to 1 do
   begin
     S_wrong := S_wrong + GetS_Wrong;
     S_wrong2 := S_wrong2 + GetS_Wrong2;
     S_correct := S_correct + GetS_Correct;
   end;
   Write('Wrong: ');
   WriteVec(S_wrong);
   Writeln(' expected: 12');

   Write('Wrong2: ');
   WriteVec(S_wrong2);
   Writeln(' expected: 12');

   Write('Correct: ');
   WriteVec(S_correct);
   Readln;
end.

The output is (Delphi 10):

Wrong: 22 expected 12
Wrong2: 11 expected: 12
Correct: 12

You see that in case of GetS_Wrong and GetS_Wrong2 even Delphi has 
garbage data in Result. And it is correct like this.

Ondrej



More information about the fpc-devel mailing list