Ada 按指针使用变量记录
我只是不明白为什么下面的方法不起作用。有人能帮我修一下吗?它抱怨(在运行时): 引发的约束_错误:variant2。adb:21鉴别检查失败Ada 按指针使用变量记录,ada,Ada,我只是不明白为什么下面的方法不起作用。有人能帮我修一下吗?它抱怨(在运行时): 引发的约束_错误:variant2。adb:21鉴别检查失败 procedure Variant2 is type POWER is (NONE,GAS, STEAM); type VEHICLE (Engine : POWER := NONE) is record Model_Year : INTEGER range 1888..1992; case Engine is
procedure Variant2 is
type POWER is (NONE,GAS, STEAM);
type VEHICLE (Engine : POWER := NONE) is
record
Model_Year : INTEGER range 1888..1992;
case Engine is
when NONE => null;
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
end case;
end record;
Works : VEHICLE;
Works_Not : access VEHICLE := new VEHICLE;
begin
Works := (GAS,1980,4); -- (1)
Works_Not.all := (GAS,1981,8); -- (2)
end Variant2;
(1) 正在工作,但(2)不工作
提前谢谢 RM明确声明“如果指定的类型是复合的,[…]创建的对象受其初始值的约束(即使指定的子类型不受默认值的约束)。”() 这意味着您必须重新分配访问类型
Works\u Not:=新车”(GAS,1981,8)代码>
(当然,您应该首先取消分配旧的访问值(请参阅),但我将此作为练习)
更新:如评论中所述
下面是一个您可以使用的示例:
with Ada.Text_IO;
procedure Array_Of_Aliased is
type POWER is (NONE, GAS, STEAM);
type VEHICLE(Engine : POWER := NONE) is
record
Model_Year : Integer range 1888..1992;
case Engine is
when NONE => null;
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
end case;
end record;
-- array of aliased elements
type Vehicle_Array is array(1..5) of aliased VEHICLE;
-- the access type need to be "all" or "constant" in order to access aliased values
type Vehicle_Access is access all VEHICLE;
Vehicles : Vehicle_Array;
Works : Vehicle_Access;
begin
-- access to the first element of the array. Can't change discriminant this way...
Works := Vehicles(1)'Access;
Ada.Text_IO.Put_Line(POWER'Image(Works.Engine));
-- However, using the array, we _can_ change the discriminant, since it's _not_ an access value
Vehicles(1) := (STEAM, 1890, 20, True);
Vehicles(2) := (GAS, 1981, 8);
Ada.Text_IO.Put_Line(POWER'Image(Works.Engine));
-- We can still update the record elements using the access value, as long as the discriminant stays the same
Works.all := (STEAM, 1900, 15, False);
end Array_Of_Aliased;
RM明确声明“如果指定的类型是复合的,[…]创建的对象受其初始值的约束(即使指定的子类型不受默认值的约束)。”()
这意味着您必须重新分配访问类型
Works\u Not:=新车”(GAS,1981,8)代码>
(当然,您应该首先取消分配旧的访问值(请参阅),但我将此作为练习)
更新:如评论中所述
下面是一个您可以使用的示例:
with Ada.Text_IO;
procedure Array_Of_Aliased is
type POWER is (NONE, GAS, STEAM);
type VEHICLE(Engine : POWER := NONE) is
record
Model_Year : Integer range 1888..1992;
case Engine is
when NONE => null;
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
end case;
end record;
-- array of aliased elements
type Vehicle_Array is array(1..5) of aliased VEHICLE;
-- the access type need to be "all" or "constant" in order to access aliased values
type Vehicle_Access is access all VEHICLE;
Vehicles : Vehicle_Array;
Works : Vehicle_Access;
begin
-- access to the first element of the array. Can't change discriminant this way...
Works := Vehicles(1)'Access;
Ada.Text_IO.Put_Line(POWER'Image(Works.Engine));
-- However, using the array, we _can_ change the discriminant, since it's _not_ an access value
Vehicles(1) := (STEAM, 1890, 20, True);
Vehicles(2) := (GAS, 1981, 8);
Ada.Text_IO.Put_Line(POWER'Image(Works.Engine));
-- We can still update the record elements using the access value, as long as the discriminant stays the same
Works.all := (STEAM, 1900, 15, False);
end Array_Of_Aliased;
正如egilhh所说,当您使用new
分配鉴别记录时,您无法更改所分配记录的鉴别记录,即使您可以对该类型的变量(与分配的记录相反)执行此操作。这条规则自《美国残疾人法案》第83条以来一直存在。其基本原理是,我相信,它允许编译器在分配记录时优化空间。在您的示例中,如果我们假设所有字段(包括判别式)都是1个字,那么如果ENGINE=NONE,记录将是2个字,如果ENGINE=GAS,记录将是3个字,如果ENGINE=STEAM,记录将是4个字。当Works\u Not
被初始化时,它被初始化为NONE
,这意味着堆上可能只需要2个字(注意:编译器不需要以这种方式进行优化)。如果它只使用2个字,那么将记录重新分配到ENGINE=GAS的记录将是一场灾难——您将溢出先前分配的区域,并践踏其他数据
这是否是一个好的语言设计决定,我不能说;我不知道需要多少编译器和多少应用程序来利用这种优化。33年前有人认为这会有用,他们一定有很好的理由这么想
这一限制令人恼火,但并非不可逾越。我以前肯定多次遇到过它,但简单的答案是将它包装在另一张唱片中
type VEHICLE_DATA (Engine : POWER := NONE) is
record
Model_Year : INTEGER range 1888..1992;
case Engine is
when NONE => null;
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
end case;
end record;
type VEHICLE is record
Data : VEHICLE_DATA;
end record;
Now_Works : access VEHICLE := new VEHICLE; -- still sets ENGINE=NONE
Now_Works := (Data => (Gas, 1981, 8)); -- legal
Now_Works.Data := (Gas, 1981, 8); -- legal, does the same thing
这些都是可以的,因为分配的记录是车辆,而不是鉴别记录。可以这样更改子组件的判别式。这就是我绕过规则的方法。正如egilhh所说,当您使用new
分配鉴别记录时,您不能更改所分配记录的鉴别记录,即使您可以对该类型的变量(与分配的记录相反)执行此操作。这条规则自《美国残疾人法案》第83条以来一直存在。其基本原理是,我相信,它允许编译器在分配记录时优化空间。在您的示例中,如果我们假设所有字段(包括判别式)都是1个字,那么如果ENGINE=NONE,记录将是2个字,如果ENGINE=GAS,记录将是3个字,如果ENGINE=STEAM,记录将是4个字。当Works\u Not
被初始化时,它被初始化为NONE
,这意味着堆上可能只需要2个字(注意:编译器不需要以这种方式进行优化)。如果它只使用2个字,那么将记录重新分配到ENGINE=GAS的记录将是一场灾难——您将溢出先前分配的区域,并践踏其他数据
这是否是一个好的语言设计决定,我不能说;我不知道需要多少编译器和多少应用程序来利用这种优化。33年前有人认为这会有用,他们一定有很好的理由这么想
这一限制令人恼火,但并非不可逾越。我以前肯定多次遇到过它,但简单的答案是将它包装在另一张唱片中
type VEHICLE_DATA (Engine : POWER := NONE) is
record
Model_Year : INTEGER range 1888..1992;
case Engine is
when NONE => null;
when GAS => Cylinders : INTEGER range 1..16;
when STEAM => Boiler_Size : INTEGER range 5..22;
Coal_Burner : BOOLEAN;
end case;
end record;
type VEHICLE is record
Data : VEHICLE_DATA;
end record;
Now_Works : access VEHICLE := new VEHICLE; -- still sets ENGINE=NONE
Now_Works := (Data => (Gas, 1981, 8)); -- legal
Now_Works.Data := (Gas, 1981, 8); -- legal, does the same thing
这些都是可以的,因为分配的记录是车辆,而不是鉴别记录。可以这样更改子组件的判别式。这就是我绕过规则的方法。谢谢。我明白,但我帮不了我。我使用的数据对象是数组的元素。它们通过指针相互引用,并且必须能够通过索引和指针进行处理。这是因为我想用一个标记和扫描收集器对它们进行垃圾收集。(标记通过树遍历和线性遍历完成,并将未标记的节点排入可分配节点的“自由列表”中。)这是Lisp语言实现的一部分。也就是说,不做新的(..)就不可能直接赋值吗?您不能更改访问值的判别式,只能记录成员。但是,您可以拥有一个别名(非访问)元素数组,允许您更改数组的元素,并且仍然可以通过访问值对元素进行寻址。我马上更新我的答案谢谢。我明白,但我帮不了我。我使用的数据对象是数组的元素。它们通过指针相互引用,并且必须能够通过索引和指针进行处理。这是因为我想用一个标记和扫描收集器对它们进行垃圾收集。(标记通过树遍历和线性遍历完成,并将未标记的节点排入可分配节点的“自由列表”中。)这是Lisp语言实现的一部分。也就是说,不可能直接赋值吗