问题描述
我一直在阅读将参数传递给过程和函数的各种方法,但是对于var
和constref
应用于记录的语义仍然感到困惑(这与语言的免费Pascal方言v3.x。)
我的理解是:
-
var
和constref
都强制通过引用传递参数 -
var
建议可以更改参数,而const[ref]
则不能
这种区别对于简单类型(例如数字和字符串)有意义,但是结构化类型呢?假设我有以下记录:
A = record
x: Integer;
end;
以及对A
起作用的过程,如下所示:
procedure proc(var rec: A);
begin
rec.x := 42;
end;
即proc
不会 重新分配rec
,但是确实会产生副作用,因为它会改变其形式参数的字段。我可以使用fpc 3.0.4
或var
在constref
下进行编译,但是我的直觉是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]
是取消引用该指针的语法糖。
解决方法
您链接到的文档说明constref
与const
的不同之处仅在于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的安装中编译,则应报告该编译器错误。但是,这似乎与我观察到的不一致,因此我建议您仔细地重新检查您的发现。