ADA 95教程 访问类型变量
访问类型是不一样的
Example program ------> e_c13_p1.ada
-- Chapter 13 - Program 1 with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Access1 is type POINT_SOMEWHERE is access INTEGER; Index, Arrow, There : POINT_SOMEWHERE; begin Index := new INTEGER; Index.all := 13; Put("The value is"); Put(Index.all, 6); New_Line; Arrow := new INTEGER; Arrow.all := Index.all + 16; There := Arrow; Put("The values are now"); Put(Index.all, 6); Put(Arrow.all, 6); Put(There.all, 6); New_Line; There.all := 21; Put("The values are now"); Put(Index.all, 6); Put(Arrow.all, 6); Put(There.all, 6); New_Line; end Access1; -- Result of execution -- The value is 13 -- The values are now 13 29 29 -- The values are now 13 21 21
access type变量不同于我们遇到的所有其他类型,因为它实际上不是一个可以存储一段数据的变量,而是包含另一段可以被操作的数据的地址。最好的老师总是一个例子,因此请检查名为e_c13_p1.ada的文件中的示例程序,其中包含一些访问类型变量。
声明访问类型变量
在第7行中,我们声明了一个新类型,一个访问类型。与所有类型一样,保留的字类型都会被指定,后面是类型名称,然后保留词is和类型定义。类型定义以保留字access开始,该访问类型表示access类型变量,然后是我们希望访问的类型。我们希望访问的类型可以是在程序中这个点之前声明的任何类型,可以是预先共享的类型,也可以是我们声明的类型。由于Ada中没有可用的预共享访问类型,因此必须声明希望使用的所有访问类型。
新类型用于在第8行中声明三个访问变量。没有声明实际变量,只访问内存中实际上还不存在的三个位置。访问类型变量不会像预期的那样存储一个整数值,而是存储位于计算机内存地址空间中某个位置的整数值的地址。注意,当声明三个访问变量时,它们会自动初始化为null,Ada中的所有访问变量也是如此。
图13-1以图形方式说明了此时系统的状况。中心有点的框描述访问变量,并使用空框来描述标量变量。
我们需要一些数据来指出
当我们开始程序的可执行部分时,我们没有数据可访问,因此我们使用新的保留字在第11行创建一些数据存储。这告诉系统去某个地方,创建一个不带名称的整型变量,并使名为Index的访问变量指向这个新变量。新变量的有效地址被分配给访问变量Index,但新变量仍然没有赋值。
第12行告诉系统使用一种非常奇怪的方法将13的值分配给新变量。对于本章的前两个示例程序,我们将简单地说,所有变量的值都设置为指示值,即13。最终结果是,名为Index的访问变量指向内存中没有名称但包含13的值的某个位置。图13-2说明了我们的现状。
第13行到第15行表明,可以使用本教程前面所有课程中使用的相同方法显示此变量的值。如果您记得将.all添加到access变量中,那么您将引用存储在访问位置的数据。
第17行用于在内存中的某个地方创建另一个整数类型的变量,箭头指向它,但它还没有包含值。下一行表示取存储在索引点所在位置的值,向其添加16,并将结果(应该是29)存储在箭头指向的位置。箭头实际上“访问”变量,但是使用单词点有点描述性,特别是当您是Pascal或C程序员时。
第三个访问变量
我们还没有使用名为There的access变量,因此我们在第19行中指示系统使其指向Arrow当前正在访问的同一段数据。由于未能将all添加到两个访问变量中,我们将访问地址分配给它们,而不是箭头访问的值。如果其中只有一个在第19行追加了all,则会出现类型冲突,从而导致编译器错误消息。所有三个访问变量都在某处访问某些数据,因此我们可以使用它们的.All符号来显示所有三个值。当前数据空间的图形表示见图13-3。
请注意,实际上只有两个变量,因为两个访问变量指向同一块数据。当第26行中的一个变量发生变化时,说明了这一点,并且当打印数据时,两个值明显不同。
一定要编译并运行这个程序。当您认为您理解它时,看看是否可以修改它,使所有三个访问变量都指向同一个数据段。
访问整型和浮点型变量
Example program ------> e_c13_p2.ada
-- Chapter 13 - Program 2 with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO; procedure Access2 is type POINT_TO_INT is access INTEGER; Index, Arrow : POINT_TO_INT; type POINT_TO_FLOAT is access FLOAT; X, Y, Z : POINT_TO_FLOAT; begin Index := new INTEGER'(173); Arrow := new INTEGER'(57); Put("The values are"); Put(Index.all, 6); Put(Arrow.all, 6); New_Line; Index.all := 13; Arrow.all := Index.all; Index := Arrow; X := new FLOAT'(3.14159); Y := X; Z := X; Put("The float values are"); Put(X.all, 6, 6, 0); Put(Y.all, 6, 6, 0); Put(Z.all, 6, 6, 0); New_Line; X.all := 2.0 * Y.all; Put("The float values are"); Put(X.all, 6, 6, 0); Put(Y.all, 6, 6, 0); Put(Z.all, 6, 6, 0); New_Line; end Access2; -- Result of execution -- The values are 173 57 -- The float values are 3.141590 3.141590 3.141590 -- The float values are 6.283180 6.283180 6.283180
检查名为e_c13_p2.ada的文件,以获取访问类型变量的其他一些示例。我们首先声明两个访问整型变量的访问变量和三个访问浮点型变量的访问变量。应该指出的是,尝试使用错误的访问变量类型访问变量是非法的,这对您来说可能并不奇怪。显式类型转换可以涉及数据类型,但不能涉及访问类型。
第14行引入了一个新的构造,即在创建变量时初始化变量。使用类似于限定的形式,在内存中的某处创建一个整数类型变量,初始化为173,并为名为Index的访问变量分配其地址,使其指向该变量,或访问该变量。执行第15行后,数据空间如图13-4所示。
请务必注意第21行和第22行中表达式之间的差异。在第21行中,存储在索引点处的值存储在箭头指向的位置。然而,在第22行中,使访问变量索引指向访问变量箭头指向的同一位置。
浮点型访问变量
第24行说明了使用Pi值初始化的浮点型变量的创建。由于在第25行和第26行中使用了访问变量名,没有附加all,因此所有三个FLOAT类型的访问变量都被指定访问同一个变量,并显示一些结果。图13-5说明了此时系统的状况。
单个浮点型变量在第33行中加倍,并以三种不同的方式再次显示。一定要编译并执行这个程序。
访问记录变量
Example program ------> e_c13_p3.ada
-- Chapter 13 - Program 3 with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Unchecked_Deallocation; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Access3 is type MY_RECORD is record Age : INTEGER; Initial : CHARACTER; Sex : CHARACTER; end record; type ACCESS_MY_DATA is access MY_RECORD; procedure Free is new Ada.Unchecked_Deallocation(MY_RECORD, ACCESS_MY_DATA); Myself : ACCESS_MY_DATA; Friend : ACCESS_MY_DATA := new MY_RECORD'(30, 'R', 'F'); Result : BOOLEAN; begin Myself := new MY_RECORD; Myself.Age := 34; Myself.Initial := 'D'; Myself.Sex := 'M'; Friend := new MY_RECORD'(31, 'R', 'F'); Put("My age is"); Put(Myself.Age, 3); Put(" and my initial is "); Put(Myself.Initial); New_Line; Friend.all := Myself.all; Result := Friend.all = Myself.all; -- TRUE because of line 43 Result := Friend = Myself; -- FALSE because they point -- to different things. Free(Myself); Free(Friend); end Access3; -- Result of execution -- My age is 34 and my initial is D
检查名为e_c13_p3.ada的示例程序,了解访问变量的一些其他用法。这个程序首先定义一个记录类型,然后定义一个可用于访问此记录类型的数据的访问类型。暂时忽略第16行中的Free程序。在第19行中,我们声明了一个名为Myself的变量,它是一个访问MY_RECORD类型的变量的访问变量。因为记录还不存在,所以access变量实际上没有指向任何地方。根据Ada定义,创建的access变量将被初始化为null值,这意味着它不指向任何地方。这个值可以测试一些值,我们将在后面看到。Ada程序中使用的所有访问变量,不管它们是如何声明的,最初都将被赋值为null。这是正确的,除非它们被特别初始化为某个值,如第20行所示。
第20行非常有趣,因为我们声明了一个名为Friend的访问变量,并通过在内存中的某处创建一个新记录来初始化访问变量,然后将记录本身初始化为位置聚合中给定的值。最后,使访问变量Friend指向新创建的记录。允许创建一个新记录,但是省略初始化,在程序的可执行部分提供初始值,就像我们处理名为Myself的变量一样。最后声明一个布尔类型变量供以后使用。图13-6展示了我们当前的数据空间。
使用记录访问变量
在第26行中,我们创建了一个MY_RECORD类型的新变量,它由三个单独的字段组成。这三个字段的分配方式与我们研究记录的那一章中的分配方式大致相同,因此这应该不会给您带来任何问题。在第32行中,我们在某处创建另一条新记录,并将其初始化为给定的值,并使名为Friend的变量指向它或访问它。见图13-7。
现在我们有一些丢失的变量
考虑到访问变量朋友已经有了一些数据,我们告诉系统使它指向这个新创建的记录。它以前指向的记录现在在记忆中的某个地方,但没有指向它,所以实际上它丢失了。我们不能在里面存储任何东西,也不能读取存储在其中的数据。更重要的是,我们无法释放这些内存位置以供进一步使用,因此,我们的程序将完全失去空间。当然,操作系统会在程序终止时清理掉的所有变量,这样数据不会永远丢失。我们应该看到,我们不会因为笨拙的编程而丢失内存空间,因为Ada无法检查是否重新分配了访问变量。
第40行展示了下一个有趣的事情,其中我访问的记录的所有字段都分配给朋友访问的记录的所有字段。现在,Ada的设计者选择引用所有符号访问的数据,这是有意义的,它引用了访问变量指向的所有数据。应该指出,随着您对Ada的经验的积累,您将发现几乎所有访问类型变量都用于访问记录,并且很少(如果有的话)将访问标量变量。
带访问变量的布尔操作
访问变量访问的记录可以与常规记录一样,比较相等或不平等,并且只有当一个记录中的所有字段都等于另一条记录中的相应字段时,它们才相等。因此,第42行将导致TRUE,因为第40行中的分配使得记录相同。访问变量也可以相互比较,只有当它们都指向同一个对象时,才会产生TRUE。在第43行中,结果是错误的,因为它们指向不同的记录,即使它们所指的记录恰好相等。
什么是未选中的解除分配?
未选中的过程Unchecked_Deallocation 是Ada的必需部分,因此编译器编写器已向您提供了此过程作为库的一部分。通过使用本程序中所示的此过程,系统可以释放任何动态分配的数据以供重用。您必须首先实例化第16行中所示的通用过程的副本,并将其命名为您选择的任何可用标识符。必须提供两种类型作为参数,第一种是要解除分配的对象类型,第二种类型是访问类型。通常使用Free名称,因为该名称用于Pascal和C中的等效过程。
要实际解除分配某些存储,您使用访问要释放的存储的访问变量的名称作为过程的唯一参数Free,如第46和47行所示。然后,该存储可供程序的其他部分重用。
关于存储的解除分配和实现方式还有很多要说的,但是在您获得更多Ada经验之后,细节将保留到第23章。在那之前,由于你对Ada的知识有限,你可能不会编写需要这些信息的程序。一定要编译并执行这个程序。
垃圾收集
另一种解除访问变量访问的数据的方法是将null值分配给访问变量。这将导致动态分配的变量没有访问它们的访问变量,然后它们是不可用的,或者垃圾。Ada实现可以实现垃圾收集器,以搜索未访问的数据并回收存储以供进一步使用。垃圾收集器的实现是可选的,根据ARM。在第25章中,将进一步讨论处理和垃圾收集。
访问数组数据
Example program ------> e_c13_p4.ada
-- Chapter 13 - Program 4 with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Unchecked_Deallocation; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Access4 is type MY_ARRAY is array(3..8) of INTEGER; type POINT_TO_ARRAY is access MY_ARRAY; procedure Free is new Ada.Unchecked_Deallocation(MY_ARRAY, POINT_TO_ARRAY); List_Of_Stuff : MY_ARRAY := (34, 12, -14, 1, 27, -11); There : POINT_TO_ARRAY; Here : POINT_TO_ARRAY; begin There := new MY_ARRAY; There.all := List_Of_Stuff; Here := There; for Index in MY_ARRAY'RANGE loop Put(List_Of_Stuff(Index), 6); Put(There.all(Index), 6); Put(Here.all(Index), 6); New_Line; end loop; Free(There); end Access4; -- Result of execution -- 34 34 34 -- 12 12 12 -- -14 -14 -14 -- 1 1 1 -- 27 27 27 -- -11 -11 -11
名为e_c13_p4.ada的示例程序给出了使用访问变量访问数组的示例。这里唯一可以被认为是新的东西是第20行中的赋值,其中数组 List_Of_Stuff,被赋值给变量,该变量由There的access变量访问。注意,在这条语句中实际分配了6个整型值。
请注意,这里还举例说明了Unchecked_Deallocation的取消分配。程序应该简单易懂,理解之后,编译并执行它。
访问类型变量数组
Example program ------> e_c13_p5.ada
-- Chapter 13 - Program 5 with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Access5 is type MY_RECORD is record Age : INTEGER; Initial : CHARACTER; Sex : CHARACTER; end record; type ACCESS_MY_DATA is access MY_RECORD; Myself : ACCESS_MY_DATA; Class : array(1..10) of ACCESS_MY_DATA; begin Myself := new MY_RECORD; Myself.Age := 34; Myself.Initial := 'D'; Myself.Sex := 'M'; for Index in 1..10 loop Class(Index) := new MY_RECORD; Class(Index).all := Myself.all; end loop; Class(3).Age := 30; Class(3).Initial := 'A'; Class(5).Initial := 'Z'; Class(8).Initial := 'R'; Class(6).Sex := 'F'; Class(7).Sex := 'F'; Class(2).Sex := 'F'; for Index in 1..10 loop Put("The class members age is"); Put(Class(Index).Age, 3); if Class(Index).Sex = 'M' then Put(" and his initial is "); else Put(" and her initial is "); end if; Put(Class(Index).Initial); New_Line; end loop; end Access5; -- Result of execution -- The class members age is 34 and his initial is D -- The class members age is 34 and her initial is D -- The class members age is 30 and his initial is A -- The class members age is 34 and his initial is D -- The class members age is 34 and his initial is Z -- The class members age is 34 and her initial is D -- The class members age is 34 and her initial is D -- The class members age is 34 and his initial is R -- The class members age is 34 and his initial is D -- The class members age is 34 and his initial is D
检查程序e_c13_p5.ada中的一个示例,该示例包含在第17行中声明的访问变量数组。名为Class的变量总共由十个访问类型变量组成,其中任何一个都可以用来指向 MY_RECORD.类型的变量。第26行到第29行中的循环用于首先创建一个记录变量,然后为十个访问变量中的每一个为所创建的记录变量的字段赋值。由于每条记录由三个子字段组成,因此在这个循环中总共创建了30个独立的变量并赋值。为了便于说明,在第31行到第37行中重新分配了一些变量的值,整个名为Class的数组显示在监视器上。
请注意,第17行中声明的数组是匿名类型数组。
编译并运行这个程序,确保您理解结果的打印输出。当我们在本教程后面学习一些高级编程技术时,access变量将非常重要。
访问静态对象
Example program ------> e_c13_p6.ada
ADA95能够使用访问变量访问局部或全局变量。Ada 83不允许这样做。为了做到这一点,有必要通过使用关键字all将access变量标识为能够访问局部或全局变量的变量,如示例程序的第13行到第15行中所做的那样。此外,要访问的变量必须用新的关键字别名标识为可访问,如本程序第18、21和24行所示。这是一种很容易被滥用的做法,因此Ada的设计者强制您在实际执行操作之前识别访问变量和要访问的变量。这迫使你仔细考虑你在做什么。指针的使用和滥用是其他一些语言中最薄弱的环节,导致很难发现错误。
您将注意到,如果在使用此技术时缺少任何成分,Ada编译器将通过一个错误进行投诉。第30行到第32行包括在这个程序中,以说明各种使用规则。
当您使用访问变量引用局部或全局变量时,编译器必须遵循并检查另一条规则。引用的对象的存在时间必须与访问变量本身的存在时间相同或更长,否则编译器将发出错误消息。这是为了防止在访问变量所引用的对象不再存在时使用访问变量的错误。
访问子程序
Example program ------> e_c13_p7.ada
-- Chapter 13 - Program 7 with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure AcessFnc is function Double_It(In_Value : INTEGER) return INTEGER is begin return 2 * In_Value; end Double_It; function Triple_It(In_Value : INTEGER) return INTEGER is begin return 3 * In_Value; end Triple_It; function Decade_It(Number : INTEGER) return INTEGER is begin return 10 * Number; end Decade_It; type ACCESS_FUNCTION is access function(X : INTEGER) return INTEGER; Multiply : ACCESS_FUNCTION; Worker : INTEGER := 17; begin Multiply := Double_It'Access; Put("Double value is"); Put(Multiply(Worker), 6); New_Line; Multiply := Decade_It'Access; Put("Ten times value is"); Put(Multiply(Worker), 6); New_Line; Multiply := Triple_It'Access; Put("Triple value is"); Put(Multiply(Worker), 6); New_Line; end AcessFnc; -- Result of execution -- -- Double value is 34 -- Ten times value is 170 -- Triple value is 51
名为e_c13_p7.ada的示例程序是使用单个访问变量调用三个不同函数的示例。第7行到第20行中定义了三个函数,每个函数都有相同的参数数量、相同的参数类型和相同的返回类型。有了所有这些并行性,它们就可以与access变量一起使用。在第22行中,我们定义了一个与上面列出的三个函数具有相同签名的类型,最后我们声明了一个名为Multiply的新类型的访问变量。在程序的可执行部分,我们使用第31、36和41行中的access变量调用这三个函数。同一个调用会导致对不同物理函数的调用,因为每次调用时它都指向不同的函数。第29、34和39行是我们实际将地址分配给访问变量的位置。
同样的技术也可以用于过程调用,前提是每个实体的签名都是相同的。对于该过程,所有参数必须具有相同的模式。根据定义,它们都是函数中的in模式。
当您使用访问变量引用子程序时,编译器必须遵循并检查另一条规则。引用的对象的存在时间必须与访问变量本身的存在时间相同或更长,否则编译器将发出错误消息。这是为了防止在访问变量所引用的对象不再存在时使用它,这是一个明显的错误。在您认为可以重写此规则的情况下,可以使用未经检查的访问,并将其记录在ARM中。
不鼓励使用任何未经检查的东西。
编程练习
1.声明一个名为BOX_TYPE的记录,该记录由三个FLOAT类型元素Length、Width和Height组成。使用新函数创建三个名为Small、Medium和Large的框,并在所有九个字段中存储合适的值。显示有关三个盒子的数据以及每个盒子的体积。(Solution)
2.将 Unchecked_Deallocation 过程添加到其他三个示例程序,即e_c13_p1.ada, e_c13_p2.ada, and e_c13_p5.ada,并释放所有分配的变量。(Solution 1)(Solution 2)(Solution 3)
---------------------------------------------------------------------------------------------------------------------------
原英文版出处:https://perso.telecom-paristech.fr/pautet/Ada95/a95list.htm
翻译(百度):博客园 一个默默的 *** 的人