Wednesday, April 11, 2007

The most important Delphi setting

I have my favourite Delphi setting. A setting that is not enabled by default, but which I always turn on. A setting that many developer's don't use and many can't live without. A setting that you should use. Always.

I'm not telling you which setting this is. Yet.

Look at this code fragment first.

  i := 1;                
while (s[i] <> s[i+1]) and (i < Length(s)) do
i := i + 1;
There is a problem in this code. Can you spot it?



This small fragment accesses s[Length(s) + 1], which is an undefined value and may even lie outside of allocated virtual memory and cause an Access Violation during program execution. This happens because of a simple bug - tests in the while loop should be reversed so that i < Length(s) proposition is tested first.

  i := 1;                
while (i < Length(s)) and (s[i] <> s[i+1]) do
i := i + 1;

The really big problem with this code is that Delphi doesn't report any error when you run it. At least with default settings it doesn't.


And now we came to my favourite Delphi setting - Range checking.


Select Project.Options and check Range checking.


Click OK and rebuild the project. Run it again.


See! Delphi can find such errors, it just needs a little motivation!


I strongly recommend to enable range checking for all new projects. Close all projects in Delphi, then select Projects.Default Options.Delphi for Win32 and check Range checking (in BDS 2006; instructions for other IDE versions may differ).


If range checking is so good, then why is it not turned on by default? I suspect that it was originally disabled because of speed issues on slow CPUs. Range checking can quickly add 10% to the execution time if your code uses string and array indices a lot. Nowadays this is hardly noticeable but by enabling range checking, old code may break in very interesting ways. Especially if it contains some range-related bugs ...


If you want to execute some time-critical code without range checking, you can put {$R-} before it (to disable rangle checking) and {$R+} after. Or, if you like more self-descriptive commands, {$RANGECHECKS OFF} and {$RANGECHECKS ON}. And please don't try to guess what the time-critical part of your program is, use a profiler.


 


Technorati tags: , ,

9 comments:

  1. Anonymous22:59

    I've always wondered why this option still exists for Delphi .NET projects? Aren't .NET strings and arrays range-checked by default by the CLR/compiler?

    ReplyDelete
  2. I have absolutely no idea. I do 100% of development for Win32.

    ReplyDelete
  3. Can you tell me a profile name? Regards

    ReplyDelete
  4. Good profilers for Delphi are:
    AutomatedQA'a AQTime, Helmuth Adolph's ProDelphi, and my own GpProfile.

    ReplyDelete
  5. when range checking is turned on what is affected in terms of execution speed? is it the runtime speed at the IDE/Compiler/Debugger level or runtime speed of the generated executable for deployment? 10% matters if it affects EXEs for deployment.

    ReplyDelete
  6. Execution speed.

    And I disagree - 10% is nothing. Your users have CPUs that vary in speed by more than this amount.

    Even more, for most applications it won't be 10%, but much much more (even unnoticeable).

    When the slowdown is indded too big, you can still use range checking for debugging and only compile release version of your app without it. I tend to keep range checking always on - there are many bugs that only appear at customers.

    ReplyDelete
  7. I understand. Thank you. But I would love to hope there would be better options to avoid this error without a trade-off.
    Nice post.

    ReplyDelete
  8. Barton Stano17:21

    Thanks for the tip on complier settings.
    Great tip!!!!

    ReplyDelete
  9. Anonymous15:39

    if 10% really is nothing, I'll have 10% of your annual salary please. I'll take a cheque.

    Use it to find errors, but don't come to rely on it so much you end up forgetting it's there... to be willing to sacrifice a whole 10% just to make programming a little easier is quite lazy IMO.

    10% here, 10% there.. coupled with your application (inevitably) ending up having to handle 10x the amount of data you originally had in mind when writing it, and suddenly things are looking a bit slow...

    just my $0.02...

    ReplyDelete