Ada 中越界值的显式转换
Explicit conversion of out-of-bounds value in Ada
假设我声明了一些类型
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);
编译时没有范围检查?
正如所写,您的代码包含可以在编译时轻松检测到的约束违规。 pragma Suppress (All_Checks)
允许编译器抑制 runtime 检查(ARM 11.5(2)),但 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 所说,结果不是由语言定义的。最可能的行为是,编译器将生成代码,将这些值视为具有特定 "natural" 范围的整数,并且只要它们适合该范围,就会简单地复制整数。
在你的例子中:
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)
可以正常工作,因为可能有一些低级函数将 "integer" 转换为图像,它接受任何 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_SUBTYPE
的范围,但由于范围检查被禁止,因此不会导致异常。)不同的编译器在这种情况下可能有不同的行为。
假设我声明了一些类型
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);
编译时没有范围检查?
正如所写,您的代码包含可以在编译时轻松检测到的约束违规。 pragma Suppress (All_Checks)
允许编译器抑制 runtime 检查(ARM 11.5(2)),但 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 所说,结果不是由语言定义的。最可能的行为是,编译器将生成代码,将这些值视为具有特定 "natural" 范围的整数,并且只要它们适合该范围,就会简单地复制整数。
在你的例子中:
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)
可以正常工作,因为可能有一些低级函数将 "integer" 转换为图像,它接受任何 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_SUBTYPE
的范围,但由于范围检查被禁止,因此不会导致异常。)不同的编译器在这种情况下可能有不同的行为。