Umsugi
Umsugi

Reputation: 53

Error in delphi while working with functions and arrays

image

This error is occurring when I'm filling an array through a function. The function fills an array with all the details stored in a textfile. The code works in filling the array, but when I tried using it in a function it doesn't work.

type
  DynamicArray = array of string; 

Function fillarr(ArrParm: DynamicArray; FileName: String): DynamicArray;
Var
  tf: TextFile;
  sWord: String;
  i: Integer;
begin
  i := 0;
  SetLength(ArrParm, 1);
  AssignFile(tf, FileName);
  Reset(tf);
  while not eof(tf) do
  begin
    Readln(tf, sWord);
    ArrParm[i] := sWord;
    Inc(i);
    SetLength(ArrParm, i + 1);
  end;
  SetLength(ArrParm, i - 1);
  Result := ArrParm;
end;

procedure TForm4.FormShow(Sender: TObject);
Var
  arrPictures: DynamicArray;
begin
  FillArr(arrPictures, 'NameOfTheTextFile.txt');
  ShowMessage(arrPictures[1]); // Error occurs here

Upvotes: 0

Views: 800

Answers (1)

Andreas Rejbrand
Andreas Rejbrand

Reputation: 108963

You must understand that dynamic arrays are reference types, but the references are passed by value.

In TForm4.FormShow, you have a dynamic array variable arrPictures. Even though this is a local variable, it is initialized because it is a dynamic array, which is a managed type. Specifically, it is nil (a zero-length dynamic array) at the beginning. Hence, the second element arrPictures[1] does not exist at the beginning of this method.

You attempt to populate the array with your call to FillArr. However, when you pass arrPictures to this method, the reference is passed by value, so FillArr obtains a copy of the pointer to the dynamic array heap object.

Now, the SetLength procedure reallocates the dynamic array heap object, so the pointer to it has to change. In other words, the value of the ArrParm variable changes, to point to a new dynamic array heap object. The initial one, which TForm4.FormShow is aware of, is left unchanged. (But in this case it didn't exist at all, because the array was nil, of zero length.)

Hence, when fillarr returns, arrPictures is still nil, and arrPictures[1] still doesn't exist. So you get an AV trying to read it.

The solution is to make the array parameter a var parameter, so the dynamic array variable is passed by reference. This way, when FillArr changes the value of the pointer, this change will be visible to the variable in TForm4.FormShow:

function fillarr(var ArrParm: DynamicArray; FileName: string): DynamicArray;

(fillarr is a function, and it actually returns the new array. But when you call this function in TForm4.FormShow, you discard the result value, so you gain nothing by it being a function. But this means that a different solution would be not to discard the result value. But then you can remove the parameter altogether. You don't need or want two ways for the routine to return the same value. Either let it be a procedure with a var or out parameter, or let it be a function with no parameters.)


In addition to this problem, there are other issues with the code. For instance, try to think about how you read the file. Can you do this with fewer SetLength calls?

(But in a real application, one should not use legacy Pascal I/O at all, and one should "never" increase the size of a dynamic array or string one element at a time.)

Upvotes: 4

Related Questions