Ada 编程/属性/'Unrestricted Access
Unrestricted_Access
属性类似于 Access
,但省略了所有可访问性和别名视图检查。这是一个用户自担风险的属性。
对于对象,它类似于 Address
,在需要访问类型的值时,它是 Address
的理想替代。换句话说,它的效果类似于首先应用 Address
属性,然后将其未经检查地转换为所需的访问类型。
对于子程序,可以在 P'Access
非法的情况下使用 P'Unrestricted_Access
,以构造一个指向嵌套程度更高的子程序的嵌套程度较低的命名访问类型的值。只要嵌套程度更高的子程序仍然存在,此值就可以用于间接调用;一旦包含它的子程序返回,此类调用就会出错。例如
package body P is
type Less_Nested is not null access procedure;
Global : Less_Nested;
procedure P1 is
begin
Global.all;
end P1;
procedure P2 is
Local_Var : Integer;
procedure More_Nested is
begin
... Local_Var ...
end More_Nested;
begin
Global := More_Nested'Unrestricted_Access;
P1;
end P2;
end P;
当从 P2 调用 P1 时,通过 Global 的调用是正常的,但是如果在 P2 返回后调用 P1,则它将是悬空指针的错误使用。
对于对象,可以对任何类型使用 Unrestricted_Access
。但是,如果结果是访问不受约束的数组子类型的类型,则结果指针与属性的上下文具有相同的范围,并且不得返回到某个封闭的范围。例如,如果一个函数使用 Unrestricted_Access
创建一个访问不受约束数组的指针并将其返回值给调用者,则结果将涉及悬空指针。此外,仅当指针具有正常的默认“胖”表示形式时,才能使用此属性创建指向不受约束数组的指针,其中指针有两个组件,一个指向数组,另一个指向边界。如果使用大小子句强制指向不受约束数组的指针的“瘦”表示形式,其中只有一个指针的空间,则生成的指针不可用。
在 Unrestricted_Access
直接尝试为非别名对象创建瘦指针的简单情况下,编译器会拒绝该使用,因为它是非法的,如下例所示
with System; use System;
procedure SliceUA2 is
type A is access all String;
for A'Size use Standard'Address_Size;
procedure P (Arg : A) is
begin
null;
end P;
X : String := "hello world!";
X2 : aliased String := "hello world!";
AV : A := X'Unrestricted_Access; -- ERROR
|
>>> illegal use of Unrestricted_Access attribute
>>> attempt to generate thin pointer to unaliased object
begin
P (X'Unrestricted_Access); -- ERROR
|
>>> illegal use of Unrestricted_Access attribute
>>> attempt to generate thin pointer to unaliased object
P (X(7 .. 12)'Unrestricted_Access); -- ERROR
|
>>> illegal use of Unrestricted_Access attribute
>>> attempt to generate thin pointer to unaliased object
P (X2'Unrestricted_Access); -- OK
end;
但其他情况无法被编译器检测到,并且被认为是错误的。考虑以下示例
with System; use System;
with System; use System;
procedure SliceUA is
type AF is access all String;
type A is access all String;
for A'Size use Standard'Address_Size;
procedure P (Arg : A) is
begin
if Arg'Length /= 6 then
raise Program_Error;
end if;
end P;
X : String := "hello world!";
Y : AF := X (7 .. 12)'Unrestricted_Access;
begin
P (A (Y));
end;
一个普通的不受约束的数组值或一个标记为别名的约束数组对象在内存中的边界就在数组之前,因此瘦指针可以同时检索数据和边界。但在这种情况下,非别名对象 X
在字符串之前没有边界。如果类型 A
的大小子句不存在,则指针将是胖指针,其中一个组件是指向边界的指针,一切都会正常。但是,如果存在大小子句,则在调用中从胖指针到瘦指针的转换会丢失边界,因此这是错误的,程序可能会引发 Program_Error
异常。
通常,建议完全避免混合使用瘦指针和 Unrestricted_Access
,其中指定的类型是不受约束的数组。瘦指针的使用应限于移植隐式假设指针大小的遗留代码的情况,并且此类代码在任何情况下都不应使用此属性。
如果将属性应用于常量,则会出现另一种错误情况。生成的指针可用于访问常量,但尝试以这种方式修改常量所产生的效果未定义。考虑此示例
P : constant Integer := 4;
type R is access all Integer;
RV : R := P'Unrestricted_Access;
..
RV.all := 3;
在这里,我们尝试将常量 P 从 4 修改为 3,但编译器可能会或可能不会注意到此尝试,并且后续对 P 的引用可能会产生值 3 或值 4,或者如果编译器决定将 P 放入只读内存中,则赋值可能会失败。Unrestricted_Access
可以以这种方式使用的一个特殊情况是修改 in
参数的值
procedure K (S : in String) is
type R is access all Character;
RV : R := S (3)'Unrestricted_Access;
begin
RV.all := 'a';
end;
通常,这是一种冒险的方法。它可能看起来“有效”,但此类 Unrestricted_Access
的使用可能不可移植,即使是在 GNAT 的不同版本之间,因此最好避免使用。