Parameters in Depth

A value parameter of type string costs a reference-count increment/decrement (and an implicit try/finally); a value record parameter copies the whole record.

12.1 Parameter kinds

a local copy; changes are not visible to the caller Variable (by reference) var X: Integer an alias for the caller's variable outputs, in/out values const X: string read-only; compiler may pass by reference strings, records, arrays, interfaces out X: Integer like var, but the input value is discarded (managed types are cleared on entry) constref X: TRec read-only, guaranteed by reference (FPC; Delphi: const [ref]) C interop, large records var Buf / const Buf no type; use with casts, Move, SizeOf

program ParamKinds;
{$mode objfpc}{$H+}

procedure ByValue(X: Integer);
begin
  X := X * 2;           // local copy only
end;

procedure ByRef(var X: Integer);
begin
  X := X * 2;           // modifies the caller's variable
end;

procedure DivMod(A, B: Integer; out Q, R: Integer);
begin
  Q := A div B;
  R := A mod B;
end;

function CountVowels(const S: string): Integer;
var
  C: Char;
begin
  Result := 0;
  for C in LowerCase(S) do
    if C in ['a', 'e', 'i', 'o', 'u'] then
      Inc(Result);
end;

var
  N, Q, R: Integer;
begin
  N := 5;
  ByValue(N);  Writeln(N);    // 5
  ByRef(N);    Writeln(N);    // 10
  DivMod(17, 5, Q, R);
  Writeln(Q, ' r ', R);       // 3 r 2
  Writeln(CountVowels('Object Pascal'));  // 5
end.

12.2 Why const for strings and records

A value parameter of type string costs a reference-count increment/decrement (and an implicit try/finally); a value record parameter copies the whole record. const avoids both. Use const by default for strings, records, dynamic arrays, and interfaces that the routine does not modify.

12.3 Swap with var parameters

Arguments to var parameters must be variables of exactly the declared type — not expressions, constants, or a different integer type.

procedure Swap(var A, B: Integer);
var
  T: Integer;
begin
  T := A;
  A := B;
  B := T;
end;

12.4 Open array parameters

An open array parameter accepts static arrays, dynamic arrays, and array constructors of any length. Inside, indexing is always 0-based, from 0 to High(A):

Note: array of Integer in a parameter list means an open array, not a dynamic array type. To accept only dynamic arrays (e.g. to call SetLength), declare a named type: type TIntArray = array of Integer; and use var A: TIntArray.

function Sum(const A: array of Integer): Int64;
var
  I: Integer;
begin
  Result := 0;
  for I := Low(A) to High(A) do   // Low(A) is always 0
    Inc(Result, A[I]);
end;

var
  Fixed: array[1..3] of Integer = (1, 2, 3);
  Dyn: array of Integer;
begin
  Dyn := [10, 20];
  Writeln(Sum(Fixed));           // 6
  Writeln(Sum(Dyn));             // 30
  Writeln(Sum([4, 5, 6, 7]));    // 22 — open array constructor
end.

12.5 Array of const (variadic parameters)

array of const is how Format and Writeln-like routines accept mixed arguments. Each element is a TVarRec.

program VarArgs;
{$mode objfpc}{$H+}
uses SysUtils;

procedure Describe(const Args: array of const);
var
  I: Integer;
begin
  for I := 0 to High(Args) do
    case Args[I].VType of
      vtInteger:       Writeln('Integer: ', Args[I].VInteger);
      vtBoolean:       Writeln('Boolean: ', Args[I].VBoolean);
      vtChar:          Writeln('Char: ', Args[I].VChar);
      vtExtended:      Writeln('Real: ', Args[I].VExtended^:0:3);
      vtAnsiString:    Writeln('String: ', AnsiString(Args[I].VAnsiString));
      vtUnicodeString: Writeln('String: ', UnicodeString(Args[I].VUnicodeString));
      vtObject:        Writeln('Object: ', Args[I].VObject.ClassName);
    else
      Writeln('Other type: ', Args[I].VType);
    end;
end;

begin
  Describe([42, 'hello', 3.14, True, 'x']);
end.

12.6 Procedural types

Variables can hold routines:

procedure(X: Integer) / function(...): T procedure(...) of object methods (code pointer + object instance) — used by events like OnClick procedure(...) is nested nested routines (FPC {$modeswitch nestedprocvars}) reference to procedure(...) anonymous methods and anything above (Delphi; FPC trunk) — Chapter 30

program ProcTypes;
{$mode objfpc}

type
  TIntFunc = function(X: Integer): Integer;

function Twice(X: Integer): Integer;
begin
  Result := 2 * X;
end;

function Squared(X: Integer): Integer;
begin
  Result := X * X;
end;

procedure Apply(var A: array of Integer; F: TIntFunc);
var
  I: Integer;
begin
  for I := 0 to High(A) do
    A[I] := F(A[I]);
end;

var
  Data: array[0..3] of Integer = (1, 2, 3, 4);
  N: Integer;
begin
  Apply(Data, @Twice);      // objfpc mode requires '@'; delphi mode: Apply(Data, Twice)
  Apply(Data, @Squared);
  for N in Data do Write(N, ' ');   // 4 16 36 64
  Writeln;
end.