仅对一小部分代码关闭 Delphi 范围检查

khbbv19g  于 2023-03-08  发布在  其他
关注(0)|答案(3)|浏览(184)

如何关闭对文件某一部分的范围检查。关闭很容易,但是我以后如何恢复到项目设置呢?下面的伪代码应该会解释它:

Unit1;

//here's range checking on or off as per the project setting

code here...

{$R-}

//range checking is off here because the code causes range check errors

code here...

//now I want to revert to the project setting. How do I do that?

code here...

end.
n6lpvg4x

n6lpvg4x1#

请参见:IFOPT directive

{$IFOPT R+}
  {$DEFINE RANGEON}
  {$R-}
{$ELSE}
  {$UNDEF RANGEON}
{$ENDIF}
//range checking is off here because the code causes range check errors
//code here...
{$IFDEF RANGEON}
  {$R+}
  {$UNDEF RANGEON}
{$ENDIF}
nnvyjq4y

nnvyjq4y2#

将代码封装在$R指令中:

{$R-} // disable range checking
// do non-range-checked operations here
{$R+} // turn range checking back on

请注意,该指令适用于 statement 级别。您不能用它只 Package 表达式的一部分。

qni6mghb

qni6mghb3#

为什么要在发布版本中关闭它?-dan-gph
我见过太多的Delphi程序员在编写相当大的程序时从来没有激活过范围、溢出和Assert检查。当然,如果你愿意,你可以这样做,但是你的代码会有更多的bug。
我希望能说服更多的程序员立即启用这3种检查,以使程序更可靠。但是,请注意,这是要付出代价的:你的程序会变慢。2我在下面展示了一些有和没有范围检查的代码运行的实际时间比较。
重点是你可以在需要的地方用{$R -}局部关闭它。但是你可以在项目设置中全局打开它-dan-gph
就我个人而言,在Debug and Release旁边,我有第三个选项叫做PreRelease,这实际上是一个Debug版本,只有一个设置不同:"优化"是开着的。2在它仍然做检查(范围,溢出,Assert等)的时候已经足够快了。3我发布这种版本给有限数量的客户(~1000)。4如果一周后一切看起来都很好,我就用真正的发布版本交换它,在那里检查是关着的。

    • 溢出检查**

这将检查某些整数算术运算(+、-、*、Abs、Sqr、Succ、Pred、Inc和Dec)是否溢出。例如,在+(加法)运算之后,编译器将插入附加的二进制代码,以验证运算结果是否在支持的范围内。
当对整数变量的运算产生的结果超出该变量的范围时,就会发生"整数溢出"。例如,如果将整数变量声明为16位有符号整数,则其值的范围可以是-32768到32767。如果对该变量的运算产生的结果大于32767或小于-32768,发生整数溢出。
发生整数溢出时,操作的结果是未定义的,可能导致程序中出现未定义的行为:·Wrap-around结果可能会产生一个wrap-around值,这意味着数字32768实际上会存储为1,因为它比我们能存储的最大值高1个单位(32767)。·截断结果可能会被截断或修改,以适合整数类型的范围。例如,数字32768实际上将被存储为32767,因为这是我们可以存储的最高值。
未定义的程序行为是最严重的错误之一,因为它不是一个容易重现的错误,所以很难跟踪和修复。
如果您激活此选项,需要付出很小的代价:程序的速度将稍微降低。

    • IO检查**

检查I/O操作的结果。如果I/O操作失败,则会引发异常。如果关闭此开关,则必须手动检查I/O错误。如果激活此开关,则需要付出较小的代价:程序的速度将降低,但并不显著,因为与I/O操作本身所需的毫秒级时间相比,此检查引入的几微秒根本算不上什么(硬盘驱动器很慢)。

    • 范围检查**

Delphi极客称之为"最重要的Delphi设置",我完全同意。它检查所有数组和字符串索引表达式是否在定义的范围内。它还检查所有标量和子范围变量的赋值是否在范围内。
下面是一个示例代码,如果范围检查不可用,它会毁了我们的生活:

Type 
    Pasword= array [1..10] of byte; // we define an array of 10 elements
…
x:= Pasword[20];       // Range Checking will prevent the program from accessing element 20 (ERangecheckError exception is raised). Security breach avoided. Error log automatically sent to the programmer. Bruce Willis saves everyone.
    • 启用运行时错误检查**

要激活运行时错误检查,请转到"项目选项"并选中以下三个框:
在"项目选项"

中启用运行时错误检查

    • Assert**

一个好的程序员必须在代码中使用Assert来提高程序的质量和稳定性。说真的,伙计!你真的需要使用Assert。
Assert用于检查在程序的某个点上应该始终为真的条件,并在不满足条件时引发异常。在SysUtils单元中定义的Assert过程通常用于执行Assert。
你可以把Assert看作是穷人的单元测试。我强烈建议你深入研究Assert。它们非常有用,而且不需要像单元测试那样多的工作。
典型示例:

SysUtils.Assert(Input <> Nil, ‘The input should not be nil!’);

但是为了让程序检查我们的Assert,我们需要在项目设置-〉编译器选项中激活这个特性,否则它们将被忽略,就像它们不在我们的代码中一样。确保你理解我刚才所说的含义!例如,如果我们在Assert中调用有副作用的例程,我们会搞砸。在下面的例子中,在调试过程中,当Assert打开时,Test()函数将被执行,并且"This was executed"将出现在备忘录中。2然而,在发布过程中,该文本将不会出现在备忘录中,因为现在Assert被简单地忽略了。3恭喜,我们刚刚使程序在调试/发布模式下有了不同的行为。

function TMainForm.Test: Boolean;
begin
 Result:= FALSE;
 mmo.Lines.Add('This was executed');
end;

procedure TMainForm.Start;
VAR x: Integer;
begin
 x:= 0;
 if x= 0
 then Assert(Test(), 'nope');
end;

下面是一些如何使用它的示例:

1要检查输入参数是否在0..100范围内:

procedure DoSomething(value: Integer);
begin
  Assert((value >= 0) and (value <= 100), 'Value out of range');
  …
end;

2在使用指针之前检查它是否不为空:

Var p: Pointer;
Begin
  p := GetPointer;
  Assert(Assigned(p), 'Pointer is nil');
   …
End;

3在继续之前检查变量是否具有特定值:

var i: Integer;
begin
   i := GetValue;
   Assert(i = 42, 'Incorrect response to “What is the answer to life”!');
  …
end;

Assert也可以通过在项目选项中定义NDEBUG符号或使用{$D -}编译器指令来禁用。
在某些情况下,我们还可以使用Assert作为处理错误和异常的更优雅的方式,因为它更具可读性,而且它还包括一个自定义消息,这将帮助开发人员了解哪里出错了。
就我个人而言,我经常在例程的顶部使用它来检查参数是否有效。
激活这个特性会(自然地)使你的程序变慢,因为...嗯,有一行额外的代码要执行。
"没有什么是免费的"
一切美好的事物都是有代价的(幸运的是,我们的代价很小):启用运行时错误检查和Assert会降低程序的速度,并使程序变得更大。
现在的电脑有很多内存,所以内存的微小增加是无关紧要的,所以我们先把它放在一边,但让我们看看速度,因为这不是我们可以轻易忽视的:

Type                 Disabled   Enabled
Range checking       73ms         120ms
Overflow checking    580ms        680ms
I/O checking         Not tested   Not tested

正如我们所看到的,程序的速度受到这些运行时检查的强烈影响。如果我们有一个程序,速度是关键,我们最好只在调试期间激活"运行时错误检查"。我所做的,我也让它在第一个版本激活,并等待几个星期。如果没有错误报告,然后我发布了一个更新,其中"运行时错误检查"关闭。
就我个人而言,我让"IO检查"始终处于活动状态。由于这种检查而对性能的影响是微观的。

    • 重要警告:**

如果你有一个已经存在的项目写得不太好,并且你激活了下面的任何一个运行时错误检查,你的程序可能会比平常更频繁地崩溃。运行时错误检查例程并没有破坏你的程序。它总是被破坏--你只是不知道。运行时检查例程现在正在寻找所有那些代码可疑、低劣和发臭的地方。运行时检查的唯一目的是查找程序中的错误。

相关问题