是否可以在未专门启用的情况下生成范围检查错误?

Can a Range check error be generated without being specifically enabled?

一位客户报告了 Range check error 在工厂中嵌入的机器 运行 较旧的 Delphi 7 应用程序。我们(尚未)能够重现该错误。他们给我们发了一张照片:

该软件可以连续几天正常运行,而我们目前只有模糊的线索来说明这个 可能 是如何生成或可靠地复制的。我的理解是这个错误发生在两种一般情况下:

(1) An array or string has been accessed outside its bounds
(2) The variable is assigned a value out-of-range for its type

如果只有 40 个元素,则 (1) 的示例是访问数组 [50]。 (2) 的示例是将值“300”分配给无符号 BYTE。

这来自SO question 1 and SO question 2。我有很多仔细的检查工作要做,试图找出有问题的行!

我的问题是这个错误最初是如何产生的。上述两个问题都涉及 {$R+} 编译器指令。在 Project Options > Compiler > Runtime errors 中,Range checking 处于关闭状态,代码中的任何地方都没有使用 {$R+}(也没有 {$R-})。这个错误是怎么发生的?应用程序不应该崩溃或生成不同的异常吗?

是的,无需专门启用范围检查错误就可以引发它

例如只看TStream.SetSize函数:

procedure TStream.SetSize(const NewSize: Int64);
begin
  if (NewSize < Low(Integer)) or (NewSize > High(Integer)) then
    raise ERangeError.CreateRes(@SRangeError);
  SetSize(LongInt(NewSize));
end;

因此无论是否启用范围检查错误都会引发异常。你有 delphi 几个这样的函数。

我来回答问题:

Can a Range check error be generated without being specifically enabled?

但是,您在客户站点遇到的问题需要进一步调查才能解决。通常这种事情需要以下组合:

  • 异常日志记录:在发生异常时生成堆栈跟踪以准确确定调用了哪些代码的工具。
  • Tracing:记录消息,提供有关应用程序状态的线索,以帮助您进行调查。

是的,范围检查错误可以生成而无需特别启用。

  1. 可以随时使用 raise ERangeError.Create(...); 引发范围检查错误。如果调用,无论 range-check 设置的状态如何,这都会引发错误。

注意代码可以如果这样写,也遵守如下设置:

{$IFOPT R+}
  raise ERangeError.Create(...);
{$ENDIF}

但重点是一旦调用raise <SomeClass>.Create(...),就会引发异常。如果您搜索 Delphi 源代码,您会发现一些引发 ERangeError 的地方。 Loki 的回答提供了一个例子。

  1. 第二个需要注意的重要事项是 {$R} 设置不是全局的。如果您在关闭此设置的情况下编译项目,但在打开此选项编译的 DCU 中 link:则设置 仍将 开启 对于那些 DCU。

此外,可以针对特定代码段在本地更改设置。例如

{$IFOPT R-} {$R+} {$DEFINE TOGGLE_ROFF} {$ENDIF}
{ Ensure range-checking is on, but turn off again if it was already off.}
procedure MustUseRangeChecking(...);
begin
  ...
end;
{$IFDEF TOGGLE_ROFF} {$R-} {$ENDIF}

注意:您可以在自己的代码中使用 {$IFOPT} 来检查 range-checking 指令的状态。