传递记录:constref vs var

问题描述

我一直在阅读将参数传递给过程和函数的各种方法,但是对于varconstref应用于记录的语义仍然感到困惑(这与语言的免费Pascal方言v3.x。)

我的理解是:

  1. varconstref都强制通过引用传递参数
  2. var建议可以更改参数,而const[ref]则不能

这种区别对于简单类型(例如数字和字符串)有意义,但是结构化类型呢?假设我有以下记录:

  A = record
    x: Integer;
  end;

以及对A起作用的过程,如下所示:

procedure proc(var rec: A);
begin
  rec.x := 42;
end;

proc不会 重新分配rec,但是确实会产生副作用,因为它会改变其形式参数的字段。我可以使用fpc 3.0.4varconstref下进行编译,但是我的直觉是rec应该在这里声明为var以便表明该记录实例所占用的内存已被过程调用更改了?

换句话说,只要我不重新分配形式参数并且想强制通过引用传递记录,或者仅在该记录确实是不可变的意义上,则应该使用constref不写入其任何字段吗?而且,从编译器的角度来看, 的区别是什么,因为显然它无法捕捉到这样的事实,即使使用constref我也可以使通过的记录的字段进行突变。参考?

更新:

正如响应者所指出的那样,上述示例代码应该(并且确实)无法使用fpc 3.x编译;我简化了。在我的情况下,A记录保存着对动态数组的引用,正是那些我能够通过constref参数重新分配的数组元素使我感到困惑:

A = record
  x: Array of Integer;
end;

procedure proc(constref rec: A);
begin
  rec.x[0] := 42; // this works
end;

听起来像是因为动态数组本质上只是一个指针,arr[i]是取消引用该指针的语法糖。

解决方法

您链接到的文档说明constrefconst的不同之处仅在于constref自变量始终通过引用传递,而const自变量可以通过引用传递或由编译器确定的值。

那么,这段代码

procedure proc(constref rec: A);
begin
  rec.x := 42;
end;

应该导致编译错误。当我尝试在https://godbolt.org/上可用的fpc版本(2.6.0、2.6.2、2.6.4、3.0.2、3.0.4和3.2.0)中进行编译时,编译器将其拒绝为无效所有情况。

如果此代码在fpc 3.0.4的安装中编译,则应报告该编译器错误。但是,这似乎与我观察到的不一致,因此我建议您仔细地重新检查您的发现。