Paramètres "const" : le passage par la valeur ou par référence et à qoui cela sert ?

Les compilateurs Pascal comme FreePascal ou Delphi vous permettent utiliser l'option "const" afin de déclarer le paramètre d'une procédure. Quelle sont les avantages et les particularités d'utiliser cette option ?

Tout d'abord, l'option "const" prévoit la modification directe de paramètre dans la procédure. Mais vous pouvez le modifier de manière indirecte moyennant du pointeur quand même. Et c'est vous qui êtes responsables dans ce cas.

L'avantage immédiate est ce que le compilateur ne génère pas un "try..finally" implicite afin de préserver la valeur du paramètre a la fin.

D'autre part, certaine optimisation pourra passer le paramètre par la référence ce que est moins chère (et plus dangereux) par rapport de la copie passée comme une valeur.

Par contre, le passage par la référence n'est pas garantie et vous risquez avoir les problèmes si un jour le compilateur changera l'optimisation.

Voici une exemple

program Project1;
 
{$mode delphi}
 
type
  TMyRec = record
    F1, F2: integer;
  end;
 
procedure ShowRec(r: TMyRec);
begin
  writeln('F1=', r.F1, '; F2=', r.F2);
end;
 
procedure p1(const r: TMyRec);
var
  pr: ^TMyRec;
begin
  // Error: Can't assign values to const variable
  //r.F1 := 3;
  //r.F2 := 4;
  pr := @r;
  pr^.F1 := 3;
  pr^.F2 := 4;
  write('p1: '); ShowRec(r);
end;
 
procedure p2(r: TMyRec);
begin
  r.F1 := 3;
  r.F2 := 4;
  write('p2: '); ShowRec(r);
end;
 
procedure p3(var r: TMyRec);
begin
  r.F1 := 3;
  r.F2 := 4;
  write('p3: '); ShowRec(r);
end;
 
var
  rec: TMyRec;
 
begin
  rec.F1 := 1;
  rec.F2 := 2;
  ShowRec(rec);
  p1(rec);
  ShowRec(rec);
  p2(rec);
  ShowRec(rec);
  p3(rec);
  ShowRec(rec);
end.

Si on lance cette exemple sous :

  • Lazarus 1.0.4 r39423 FPC 2.6.0 x86_64-linux-gtk 2
  • Options : -MObjFPC -Scghi -O1 -g -gl -vewnhi -Filib/x86_64-linux -Fu. -FUlib/x86_64-linux/ -l

Le résultat confirme que le passage est fait par la valeur.

F1=1; F2=2
p1: F1=3; F2=4
F1=1; F2=2 <-- ici la copie n'a pas été changé
p2: F1=3; F2=4
F1=1; F2=2
p3: F1=3; F2=4
F1=3; F2=4

Et si on lance cela sous :

  • Lazarus 1.0.4 r39422 FPC 2.6.0 i386-win32-win32/win64
  • Options : -MObjFPC -Scghi -O1 -g -gl -vewnhi -Filib\i386-win32 -Fu. -FUlib\i386-win32\ -l

Le résultat confirme que le passage est fait par la référence.

F1=1; F2=2
p1: F1=3; F2=4
F1=3; F2=4 <-- modifié !
p2: F1=3; F2=4
F1=3; F2=4
p3: F1=3; F2=4
F1=3; F2=4 

Alors, la question important. Est-ce que le passage par la référence pour les paramètres "const" est fixé dans le standard de pascal (ISO 7185) ? Moi, je m'en doute et je ne trouve aucune règle ou préconisation. Sinon, c'est juste une optimisation particulière et on ne peut pas assurer que cela devrait marcher partout et toujours.

Une explication concernant FreePascal en confirme aussi : Contrary to Delphi, no assumptions should be made about how const parameters are passed to the underlying routine. In particular, the assumption that parameters with large size are passed by reference is not correct. For this the constref parameter type should be used, which is available as of version 2.5.1 of the compiler.

Donc, sous FreePascal utilisez "constref" afin d'assurer le passage par la référence. Et ne comptez pas sur le mode de passage des paramètres afin d'éviter les bugs qui difficile à trouver et le code qui est difficile à porter.