类型检查和范围检查

时间:2017-04-01 10:56:12

标签: types ada

在我正在阅读的其中一本教科书中说“在编译时检查类型的兼容性,子范围需要运行时范围检查。” 如果我说得对,那么当将新值分配给在某个子范围(子类型)中定义的变量时,必须执行运行时检查。类型不一样吗?为何如此区别?这只是编译器的默认设置吗?

2 个答案:

答案 0 :(得分:8)

确实,从类型P的子类型U分配不需要检查,因为不会出现不匹配(即使子类型与类型具有相同的范围; subtype P is U;完全合法且有用!)

另一种方式,从类型到子类型,将涉及检查,至少如果子类型是子范围。

从外部来看,具有不同范围的另一种类型也是如此。但是,有一个微妙的区别;使用类型/子类型,您可以在不进行转换的情况下将一个分配给另一个,如果不是没有检查,但是对于类型/类型,您必须在分配之前进行类型转换,并且将在转换时检查任何约束违规,而不是分配。 / p>

这可能有助于说明这一点:

procedure Drimades is
   type Upper is range 10 .. 20;
   subtype Part is Upper range 10 .. 15;
   type Lower is new Upper range 10 .. 15;

   procedure Assign_U_To_P (U : Upper; To : out Part) is
   begin
      To := U;  -- requires check
   end Assign_U_To_P;

   procedure Assign_P_To_U (P : Part; To : out Upper) is
   begin
      To := P;  -- no check needed
   end Assign_P_To_U;

   procedure Assign_U_To_L (U : Upper; To : out Lower) is
   begin
      To := Lower (U);  -- requires check
   end Assign_U_To_L;

   procedure Assign_L_To_U (L : Lower; To : out Upper) is
   begin
   To := Upper (L);  -- no check required
   end Assign_L_To_U;

   U : Upper;
   P : Part;
   L : Lower;
begin
   Assign_U_To_P (20, P);
   Assign_P_To_U (15, U);
   Assign_U_To_L (20, L);
   Assign_L_To_U (15, U);
end Drimades;

使用GNAT进行编译并使用开关-gnatG生成此中间表示,我希望不要太难解释:

procedure drimades is
   type drimades__upper is range 10 .. 20;
   [type drimades__TupperB is new short_short_integer]
   freeze drimades__TupperB []
   subtype drimades__part is drimades__upper range 10 .. 15;
   [type drimades__TlowerB is new drimades__TupperB]
   freeze drimades__TlowerB []
   type drimades__lower is new drimades__upper range 10 .. 15;

   procedure drimades__assign_u_to_p (u : drimades__upper; to : out
     drimades__part) is
   begin
      [constraint_error when
        not (u in 10 .. 15)
        "range check failed"]
      to := u;
      return;
   end drimades__assign_u_to_p;

   procedure drimades__assign_p_to_u (p : drimades__part; to : out
     drimades__upper) is
   begin
      to := p;
      return;
   end drimades__assign_p_to_u;

   procedure drimades__assign_u_to_l (u : drimades__upper; to : out
     drimades__lower) is
   begin
      [constraint_error when
        not (u in 10 .. 15)
        "range check failed"]
      to := drimades__lower(u);
      return;
   end drimades__assign_u_to_l;

   procedure drimades__assign_l_to_u (l : drimades__lower; to : out
     drimades__upper) is
   begin
      to := drimades__upper(l);
      return;
   end drimades__assign_l_to_u;

   u : drimades__upper;
   p : drimades__part;
   l : drimades__lower;
begin
   drimades__assign_u_to_p (20, p);
   drimades__assign_p_to_u (15, u);
   drimades__assign_u_to_l (20, l);
   drimades__assign_l_to_u (15, u);
   return;
end drimades;

答案 1 :(得分:2)

Simon Wright's answer从Ada及其子类型的角度解决了这个问题,这意味着某个类型的值的子集,因此不是Ada或C ++分别调用派生的 类型派生的 。 (类型 Lower Upper ... ;使Lower成为 {/ 1>}的派生,“......”定义了随后创建的子类型的更多特征。)

更一般地说,某些语言允许任何变量名称在运行时引用任何类型的对象。因此,在JavaScript中,

Upper

将返回可能令人惊讶的结果,但如果按语言判断则可以。 var a = 15, b = -1; ... a = {"foo": "bar"} ... return a + b; 在第二行分配了不同类型的新值,而a将在第三行上生成+a的内容。

在Ada,C或Swift等语言中不需要这种灵活性:用这种语言编写的程序,在任何计算机设备上运行,都意味着不会放置类似字典的对象例如,在声明的位置是某种整数类型。编译时间类型检查可防止出现这种情况。在编译时需要检测分配(或传递)对象时的任何“不尊重”。

Ada除了编译时类型检查外,还使用基于名称的类型等价。所以,

b

你得到了

type Apples is range 0 .. 20;
type Oranges is range 0 .. 20;

a : Apples := 5;
b : Oranges := 8;

return a + b;  -- Error! 

这是在编译时进行的纯类型检查。

最后,扩展了Simon Wright的例子,有时Ada甚至要求在编译时检查子类型。所涉及的短语是子类型静态 匹配,其中static表示编译时。例如,相同的边界。但是,这是发生的高级事情,例如,当指针指向堆栈上的对象并且这些对象具有与指针的不同的范围(子类型)时。