因此,当在Ada编译器上使用访问类型时,我将尝试以下经典示例:
type Node is record
next: access Node;
end record;
function reverselist(input: access Node) return access Node is
result: access Node := null;
this: access Node := input;
next: access Node := null;
begin
while this /= null loop
next := this.next;
this.next := result; -- [*]
result := this;
this := next;
end loop;
return result; -- [*]
end;
两个加星标的行会产生编译错误,因为Ada 2005的分析认为我可能会泄漏一个本地指针。 (我知道Ada 2012有更好的分析,并会接受这个。但是,我没有Ada 2012编译器。)
好的,这可以通过使用命名池访问而不是匿名access Node
轻松解决;通过使用池访问我告诉编译器指针不能引用堆栈对象。这很好。
然而,我尝试使用名为 general 的访问:
type Node;
type List is access Node;
type Node is record
next: List;
end record;
function reverselist(input: List) return access Node is
result: List := null;
this: List := input;
next: List := null;
begin
while this /= null loop
next := this.next;
this.next := result;
result := this;
this := next;
end loop;
return result;
end;
常规访问类型可以指向堆栈对象以及堆对象;那么为什么编译器拒绝第二个例子的原因与拒绝第一个例子的原因相同?
(我也有点惊讶的是,匿名access integer
变量似乎最终会出现一般访问语义。为什么我不能使用access all integer
进行匿名一般访问?)
答案 0 :(得分:3)
虽然一般虽然一般访问类型可以指向非堆对象(全局和堆栈),但有些规则会阻止List
指向堆栈。在这种情况下,由于List
未在任何子程序中声明,因此不能指向堆栈变量:
function reverselist(input: List) return access Node is
result: List := null;
this: List := input;
next: List := null;
Dummy : aliased Node;
begin
result := Dummy'Access; -- a compile-time error would occur at this point
while this /= null loop
next := this.next;
this.next := result;
result := this;
this := next;
end loop;
return result;
end;
由于规则"确保" List
永远不能指向堆栈变量,编译器不需要拒绝任何其他语句。 (我把"确保"在引号中,因为您可以使用'Unchecked_Access
绕过它们,以及Unchecked_Conversion
或其他一些方法来绕过类型系统。)< / p>
如果在子程序中声明了访问类型,它可以指向该子程序内的堆栈变量。这是因为该子访问类型的对象不能在子程序之外声明,因此在子程序完成后,这种类型的任何对象都不可能(禁止未经检查的技巧)。它还可以指向全局变量或堆对象。但它不能指向嵌套子程序中的堆栈变量。
procedure Proc is
type Node is ...
type Acc is access all Node;
N1 : aliased Node;
procedure Nested_Proc is
N2 : aliased Node;
X : Acc;
begin
X := N1'Access; -- legal
X := N2'Access; -- illegal. N2 is too deep for this access type.
end;
因此,在使用'Access
时,编译器会确保无法创建对不再存在的变量的悬挂引用。
所有这些都受可访问性规则的约束,这些规则在RM 3.10.2中有明确规定。 (我开玩笑.3.10.2非常难以理解,并且往往会在试图阅读它的人身上产生偏头痛和临界精神错乱。)
编辑:跟进您的评论:基本上,我不认为编译器正在进行任何类型的强大分析&#34;。大多数规则实际上比我听起来更简单。在Ada 2005中,大多数访问类型(包括大多数匿名访问类型)都具有&#34;级别&#34;这取决于声明类型的位置。如果访问对象的类型是级别L,则它不能获取其级别为“更深”的访问值。 (比嵌套更多)每个匿名访问都声明一个新类型,其级别通常取决于它声明的位置。在使用匿名访问类型的示例中,next
记录字段的类型是库级别,因为它在任何过程之外; result
的类型比程序内部的类型更深。 this.next := result
是非法的,因为result
更深,可能指向堆栈变量,而this.next
的级别更浅,因此不允许。对于指定的访问类型案例,this.next := result
是合法的,因为result
与next
字段一样浅,并且result
不允许指向堆栈变量首先。在Ada 2012中,我认为它有所不同,因为匿名访问对象必须跟踪他们所指向的事物的在运行时。所以this.next := result
是合法的,但是(我认为)如果result
指向堆栈变量,则会在运行时引发异常。在Ada 2012中,这些访问对象将需要一些额外的数据来允许他们进行此检查;在Ada 2005中,它们都可能只是简单的指针。 (我对Ada 2012的一些变化可能是错的。)但希望这不是太复杂。 3.10.2中的复杂性与匿名访问参数,匿名访问判别式以及您的示例与之无关的其他一些特殊情况有关。
答案 1 :(得分:1)
一种解决方案是稍微重构代码;使用扩展返回可能会为您做到:
type Node is record
next: access Node;
end record;
function reverse_list(input: access Node) return access Node is
this: access Node := input;
next: access Node := null;
begin
Return result: access Node := null do
while this /= null loop
next := this.next;
this.next := result;
result := this;
this := next;
end loop;
end return;
end reverse_list;
虽然可访问性检查可能令人沮丧,但我对它们总体上有好的看法:我们不想要悬空指针。