Initialize several variables of the same type with a single value in one declaration: a, b, c: integer = 42;. Works in var sections (global and local), typed const sections, and inline var statements. Each name gets its own independent copy of the value.
Modeswitch: multivarinit, enabled by default in {$mode unleashed}. Opt in from another mode with:
{$mode objfpc}
{$modeswitch multivarinit}var
a, b, c: integer = 42;
x, y: double = 3.14;
ok, done: boolean = false;Global variables each get their own copy of the value in the data segment. Local variables share a single internal default-value symbol; the compiler assigns it to each variable at routine entry, exactly as it does for single-variable defaults:
procedure foo;
var
la, lb, lc: integer = 10;
begin
// la = 10, lb = 10, lc = 10
end;const
MIN_X, MIN_Y, MIN_Z: integer = 0;
MAX_X, MAX_Y, MAX_Z: integer = 100;Each constant gets its own storage in the constant data section (read-only under {$J-}). Aggregate typed constants work too - the initializer is re-parsed per name via token replay:
type
TPoint = record x, y: integer; end;
const
ORIGIN, FALLBACK: TPoint = (x: 0; y: 0);With inlinevars active (default in unleashed mode) the multi-name form works at the point of use, with an explicit type or with inference:
var p, q: integer := 99; // explicit type
var i, j := 10; // both LongInt (inferred)
var s1, s2 := 'hello'; // both AnsiStringInference follows the regular inline-var rules: sub-32-bit integers promote to LongInt, character literals to the mode's default string type.
For inline declarations the expression is evaluated once into a hidden temporary and then assigned to each name. A function call in the initializer executes exactly one time:
var a, b, c := computeValue; // computeValue called once, result copied 3xEvery variable receives an independent copy. After initialization they are unrelated:
var a, b: integer = 1;
a := 42;
// b is still 1| Context | Evaluation |
|---|---|
var section (global / local) |
parsed once at compile time as a typed constant |
const section |
parsed once at compile time per name (token replay) |
inline var with := |
evaluated once at runtime, assigned to each name |
var / const sections keep the classic requirement of a compile-time constant expression; runtime expressions are allowed only in the inline form.
With a single name the behavior is identical to stock FPC - the modeswitch adds no overhead and changes no existing semantics. Code that never lists more than one name per initializer compiles bit-for-bit the same.
threadvarcannot have initializers (same as stock FPC).- The
absoluteandexternaldirectives stay single-variable only.
Lazarus CodeTools recognize the multi-name syntax in const sections; code completion resolves the type for every name in the list, so member completion works on any of them.
program multi_var_init_demo;
{$mode unleashed}
var
calls: integer = 0;
function nextSeed: integer;
begin
inc(calls);
result := calls*100;
end;
const
MIN_X, MIN_Y: integer = 0;
MAX_X, MAX_Y: integer = 100;
var
hp, mp, xp: integer = 50;
begin
writeln($'bounds: x {MIN_X}..{MAX_X}, y {MIN_Y}..{MAX_Y}');
writeln($'hp={hp} mp={mp} xp={xp}');
hp := 75;
writeln($'after hp := 75: hp={hp} mp={mp} (independent copies)');
// inline: initializer evaluated once, assigned to each name
var s1, s2, s3 := nextSeed;
writeln($'s1={s1} s2={s2} s3={s3}, nextSeed called {calls} time(s)');
var w, h: integer := 640;
writeln($'w={w} h={h}');
{$ifdef WINDOWS}readln;{$endif}
end.Output:
bounds: x 0..100, y 0..100
hp=50 mp=50 xp=50
after hp := 75: hp=75 mp=50 (independent copies)
s1=100 s2=100 s3=100, nextSeed called 1 time(s)
w=640 h=640