Compiler construction Ada中越界值的显式转换

Compiler construction Ada中越界值的显式转换,compiler-construction,compilation,ada,Compiler Construction,Compilation,Ada,假设我声明了一些类型 类型ABC\u类型的范围为0。。7. 子类型AB_子类型是ABC_类型范围0。。3. 类型DE_类型的范围为1。。4; 以下人员的预期行为是什么: abc:abc\u类型:=7; ab:ab_亚型:=ab_亚型(abc); de:de_类型:=de_类型(abc) 在编译时,如果没有范围检查?正如所写的那样,您的代码包含可以在编译时轻松检测到的约束冲突pragma Suppress(所有检查)允许编译器抑制运行时检查(),但GNAT借此机会说 notalex.adb:11:

假设我声明了一些类型

类型ABC\u类型的范围为0。。7.
子类型AB_子类型是ABC_类型范围0。。3.
类型DE_类型的范围为1。。4;

以下人员的预期行为是什么:

abc:abc\u类型:=7;
ab:ab_亚型:=ab_亚型(abc);
de:de_类型:=de_类型(abc)


在编译时,如果没有范围检查?

正如所写的那样,您的代码包含可以在编译时轻松检测到的约束冲突
pragma Suppress(所有检查)
允许编译器抑制运行时检查(),但GNAT借此机会说

notalex.adb:11:23: warning: value not in range of type "AB_Subtype" defined at line 7
notalex.adb:11:23: warning: "Constraint_Error" will be raised at run time
notalex.adb:12:20: warning: value not in range of type "DE_Type" defined at line 8
notalex.adb:12:20: warning: "Constraint_Error" will be raised at run time
然后

$ ./notalex 

raised CONSTRAINT_ERROR : notalex.adb:11 range check failed
但是,如果您使在编译时进行检查变得稍微困难一些,如这里所示

with Ada.Text_IO; use Ada.Text_IO;
procedure Notalex is

   pragma Suppress (All_Checks);

   type ABC_Type is range 0 .. 7;
   subtype AB_Subtype is ABC_Type range 0 .. 3;
   type DE_Type is range 1 .. 4;

   abc : ABC_Type;
   ab : AB_Subtype;
   de : DE_Type;

   procedure Check (Value : ABC_Type) is
   begin
      abc := Value;
      ab := AB_Subtype (abc);
      de := DE_Type (abc);
   end Check;

begin
   Check (7);
   Put_Line ("abc: " & ABC_Type'Image (abc));
   Put_Line ("ab: " & AB_Subtype'Image (ab));
   Put_Line ("de: " & DE_Type'Image (de));
end Notalex;
运行时检查被抑制。对于GNAT(在Mac OS X上),输出是

$ ./notalex 
abc:  7
ab:  7
de:  7

关于这一点,有两件事要说:第一,您的程序现在正在其设计范围之外执行,其后果可能是灾难性的;第二,幸运的是GNAT对ABC_Type'Image的实现在面对超出范围的输入时也没有失败。

如果没有范围检查,它就不是Ada,因此结果是未定义的。你可以在不同的编译器中试用,看看它们能做些什么,但是(至少在技术上)如果其中一些编译器也这么做,这是一个幸运的巧合。

正如Jacob所说,结果不是由语言定义的。最可能的行为是,编译器将生成代码,将值视为具有特定“自然”范围的整数,并且只要整数适合该范围,就会简单地复制这些整数

在您的示例中:

type ABC_TYPE is range 0 .. 7;
subtype AB_SUBTYPE is ABC_TYPE range 0 .. 3;
type DE_TYPE is range 1 .. 4;

abc : ABC_TYPE := 7;
ab : AB_SUBTYPE := AB_SUBTYPE(abc);
de : DE_TYPE := DE_TYPE(abc);
在最常用的处理器上,编译器将变量存储为1、2或4字节整数。1字节整数的范围将是0..255或-128..127——对于这种情况,这并不重要。因此,7将存储在分配给
de
的1、2或4字节整数中。然后,当程序访问de
时,它很可能只读取该整数并使用它。我希望
DE_TYPE'Image(DE)
工作正常,因为可能有一些低级函数将“整数”转换为图像,它接受任何32位(或64位)整数作为参数请注意,我不建议您依赖这种行为。鉴于我对编译器如何生成代码的了解,我认为这正是最有可能发生的情况。还要注意的是,虽然
ab
可以存储为2位整数,但编译器不太可能这样做(压缩记录或压缩数组除外),因为处理器不能处理自然整数大小

但在这种情况下:

type ABC_TYPE is range 0 .. 10000;
subtype ABC_SUBTYPE is ABC_TYPE range 0 .. 100;

abc : ABC_TYPE := 1234;
ab : AB_SUBTYPE := AB_SUBTYPE(abc);

现在,如果范围检查被禁止,编译器可能会先将
abc
截断为一个字节,然后再将其分配给
ab
。这将通过切掉上面的字节,留下210来实现,这意味着
abc
的值为210或-46,这取决于字节是否被视为有符号字节。(它仍然超出
AB_子类型的范围,但由于范围检查被抑制,因此不会导致异常。)在这种情况下,不同的编译器可能有不同的行为。

@imaluengo:Early diagnostics。只要第一次编译没有范围检查,这段代码就不应该存在。人们经常告诉我们,虫子越早被发现,消灭虫子就越容易,也越便宜;这个想法在实践中被广泛接受的程度,我不太确定。事实上,由于可以在编译时检测到违规行为,所以对范围检查没有运行时惩罚;编译器会发出警告,如果需要,只需无条件跳转到“Constraint Error”异常处理程序即可。@imaluengo我在大学里学过Ada,对此印象深刻。这是我见过的最安全的语言。(例如,定义子类型的数字的能力,即使它们具有相同的范围,也不能在没有明确转换的情况下相互分配。想想米/英尺或升/加仑。这消除了所有类别的错误,参见。)我认为Ada仍然在正确性最重要的地方使用,例如交通灯、飞机。嗯,我甚至不知道如何用C来表示一个3位整数,这样编译器就可以检查它了。我错过了K&R的什么吗?然而:我同意运行时案例更有趣,但值得注意的是,编译器也可以消除其中的许多。@imaluengo Ada远没有死,它在其预期市场上相当突出。由于
pragma Suppress
是语言的一部分,我想它就是Ada!(不管多么不明智)@SimonWright同意在不考虑范围检查的情况下进行编译,尽管在生产过程中,它生成的二进制文件要优化得多。另外,感谢您提供的示例。@notAlex您必须使用和w/o检查进行一些性能测量,以确保说“在一个更优化的二进制文件中”(不,10%并不是更优化的,除非它位于主内循环内,并且您对CPU时间预算很紧……您只能在那里关闭它们)@SimonWright-虽然
pragma Suppress
是该语言的一部分,但仍有11.5(26):“如果给定的检查已被抑制,并且出现相应的错误情况,则程序的执行是错误的。”