Recent

Author Topic: Range error if array declared in one place and none if array declared in another  (Read 7573 times)

Ian123

  • New Member
  • *
  • Posts: 28
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.      myrange = -5..5;
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     procedure Button1Click(Sender: TObject);
  18.   private
  19.  
  20.   public
  21.  
  22.   end;
  23.  
  24.  
  25.  
  26. var
  27.   Form1: TForm1;
  28.   myarray : array[myrange] of string;      //no error if use this declaration
  29.  
  30.  
  31. implementation
  32.  
  33. {$R *.lfm}
  34.  
  35.  
  36. { TForm1 }
  37.  
  38. procedure TForm1.Button1Click(Sender: TObject);
  39. var
  40.   i : integer;
  41.   //myarray : array[myrange] of string;  //error if use this declaration instead of the other
  42. begin
  43.      i := 6;      // i outside range of the type myrange
  44.      myarray[i] := 'Jack';
  45.      showmessage(myarray[i]);
  46. end;
  47.  
  48. end.                          

Hi
I declared a subrange of integers called myrange and its limits are -5 and +5
I then declare an array of strings called myarray and use myrange to set its bounds.
I then set a value for myarray[6]
As expected this throws an exception if the array is declared locally.
But if the array is declared globally it works?
Can anyone explain why this is the case?
Thanks
Windows 10 64 bit *  Lazarus 1.8.0RC1 * i386-win32-win32/win64

Handoko

  • Hero Member
  • *****
  • Posts: 3851
  • My goal: build my own game engine using Lazarus
Actually, both cases do not work.

On the declared globally case, it does not show error but it is wrong. You are lucky not to get error, but it is a hidden bug (in the future).

I recommend you to test program with range check on before you release that program to public. To do it:
Lazarus main menu > Project > Project Options > Compiler Options > Debugging > turn on: Range (-Cr)

You may also need to turn on the other options. Doing so will cause the program runs a bit slower and the file becomes larger. Usually I will disable those options on final release.

Ian123

  • New Member
  • *
  • Posts: 28
Thanks for the reply Handoko

I have to admit that I was a little surprised that this could happen*.

As you said turning on the Range (-Cr) option in the project options
caused this error to be picked up.
I also changed the code a little so the value of the index 'i' was set at runtime via
an inputbox rather than at compile time.
I then built the executable twice once with Range (-Cr) checked
and once with it unchecked.
In the build where it is checked it throws an exception if the user enters
the out of range value (6). In the build where it is unchecked it does
not throw the exception.
I'm sorry if I am asking the obvious, but does this mean
having the Range (-Cr) checked protects against this error at all times
whereas if I have the compile directive {$R+} it only protects at
compile time?
It seems like a very dangerous thing if your program can write beyond
the end of reserved space. Should it not be the default to have this
setting on?


*Perhaps the reason I am surprised is because my previous experience is VB6.
I just checked and there the default seems to be to have the equivalent of this check set
to on.
Thanks for your help and patience!


Windows 10 64 bit *  Lazarus 1.8.0RC1 * i386-win32-win32/win64

howardpc

  • Hero Member
  • *****
  • Posts: 3545
The command line switch -Cr applies rangechecking to every file in your Lazarus project.

You can use the compiler directive {$R+} (or {$R-}) to give finer control over where rangechecks are applied.

My limited testing indicates that in fact recent compilers (e.g. 3.0.2) do rangechecking whatever the {$R } setting.

The difference is that in the {$R-} state only a warning is issued, whereas with {$R+} you get an error that stops the compilation. I find no difference in the checking of subrange-typed variables between global and local declarations - both are identified if their value is out of range (unless the value is not known until runtime, when {$R+} will give rise to a runtime error).

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3857
  • I like bugs.
It seems like a very dangerous thing if your program can write beyond
the end of reserved space. Should it not be the default to have this
setting on?
Yes, while developing and testing the program. Use the debug build mode for it.
A release version can be optimized and checks removed. The release build mode is for that.
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux.

Thaddy

  • Hero Member
  • *****
  • Posts: 10516
You can use the compiler directive {$R+} (or {$R-}) to give finer control over where rangechecks are applied.
Good answer. You can do that also on a per procedure (even block) basis. Not only per unit...

Handoko

  • Hero Member
  • *****
  • Posts: 3851
  • My goal: build my own game engine using Lazarus
Read more about $R here:
https://www.freepascal.org/docs-html/prog/progsu65.html

I'm sorry if I am asking the obvious, but does this mean
having the Range (-Cr) checked protects against this error at all times
whereas if I have the compile directive {$R+} it only protects at
compile time?

Actually range check does not protect you, but it warns you when it runs into the problem. The things that protect you from errors are planning and coding carefully and testing the code before you publicize the program.

It seems like a very dangerous thing if your program can write beyond
the end of reserved space. Should it not be the default to have this
setting on?

I do not know the reasons why it is not enabled by default. But I usually turn it off (also other checkings). I do graphics programming, which use a lot of calculations and loopings. Turning range check on will cause my programs run very slow.

I plan and write my code carefully. I don't mean my codes are free from range check error, but if you're an experienced programmer you will know some good-to-do things to avoid this kind of error. I rarely use it because I know for sure what I'm writing. I sometimes turn it on but only to debug some other errors.

Of course you must test your code with range check on (also others) no matter you like it or not before you release any commercial applications.
« Last Edit: June 05, 2017, 08:58:27 pm by Handoko »

Ian123

  • New Member
  • *
  • Posts: 28
Thanks everyone, I think I have it now!

This has been very interesting for me.

Here is my understanding of how it works. Please correct me if
I have something wrong.

{$R-} will :
Put warning messages in the message window if there are out of bounds values in your source code but it will still compile the executable.
You may/may not get a run time error if an out of bounds value occurs at runtime.

{$R+} will :
Put warning messages in the message window if there are out of bounds values in your source code and won't compile the executable if there are.
Even If there are no out of bounds values in the source code and it compiles,  you will get an error  if an out of bounds value occurs at runtime.*

No range checking directive will do the same as {$R-} except there will be no warning messages during compile.

Checking the Range (-Cr) checkbox  (Project->Project Options->Compiler Options->Debugging) has the same effect as having {$R+} at the top of every unit.

The default in Lazarus is to have range checking off (For people coming from VB6, this is the opposite
of the default there).
It is a good idea to turn it on during development to detect any out of values in the source
and maybe also at runtime during testing.
Normally people turn if off when making a distributable because it can cause a program to run more
slowly.
However the {$R+} directive can be left in at specific points to cover situations where range checking
might still be desirable.
(I'd like to see an example of how it applies only to a specific expression and not to the code that follows)

Thanks again everyone.




*My initial understanding of range-checking was that it only
saved you if there were out of bounds values in the source code.
But now I see that it actually causes code to be inserted into your
compiled program to validate any values that might be inserted
at run time as well. That seems incredibly clever to me, (and yes, I don't know
anything about how compilers work!)
Windows 10 64 bit *  Lazarus 1.8.0RC1 * i386-win32-win32/win64

Nitorami

  • Sr. Member
  • ****
  • Posts: 378
Yes, range checking inserts additional code, making it somewhat slower, depending on the application.
Not sure whether this is worth mentioning but of course, none of the switches has an effect on precompiled libraries such as Classes, SysUtils, FileUtil etc.

Of note is a similar switch "integer overflow checking", easily confused with range checking. You may also find this interesting http://forum.lazarus.freepascal.org/index.php/topic,35751.0.html

Ian123

  • New Member
  • *
  • Posts: 28
Yes, range checking inserts additional code, making it somewhat slower, depending on the application.
Not sure whether this is worth mentioning but of course, none of the switches has an effect on precompiled libraries such as Classes, SysUtils, FileUtil etc.

Of note is a similar switch "integer overflow checking", easily confused with range checking. You may also find this interesting http://forum.lazarus.freepascal.org/index.php/topic,35751.0.html

 Thanks for the link. I find this all a little bit alarming!
I'd be tempted to leave all this sort of checking in place as much as I could and only turn it off if there were a problem with performance.
 I wonder what the default is in the likes of Java and c#.
Windows 10 64 bit *  Lazarus 1.8.0RC1 * i386-win32-win32/win64

Ian123

  • New Member
  • *
  • Posts: 28
The following might be a little provocative!   >:D

Wikipedia has an interesting page on this topic.

Regarding run time checking, in his 1980 Turing Award lecture, C. A. R. Hoare said:

Quote
In any respectable branch of engineering, failure to observe such elementary precautions would have long been against the law.


Quote
Mainstream languages that enforce run time checking include Ada, C#, Haskell, Java, JavaScript, Lisp, PHP, Python, Ruby, and Visual Basic



Windows 10 64 bit *  Lazarus 1.8.0RC1 * i386-win32-win32/win64

Handoko

  • Hero Member
  • *****
  • Posts: 3851
  • My goal: build my own game engine using Lazarus
{$R+} will :
Put warning messages in the message window if there are out of bounds values in your source code and won't compile the executable if there are.
Even If there are no out of bounds values in the source code and it compiles,  you will get an error  if an out of bounds value occurs at runtime.*

Not fully correct. Computer is stupid or maybe I can say the the range check is not good enough to detect out of bonds values at compile time. Turning range check on means 2 things: add range check routine in the final executable and perform range check on the compile time.

Code: Pascal  [Select][+][-]
  1. {$R+}
  2. procedure TForm1.Button1Click(Sender: TObject);
  3. var
  4.   Something: array[1..100] of string;
  5.   Z: Integer;
  6. begin
  7.   Z := 100;
  8.   if (Z <= 100) then Z := Z + 5;
  9.   Something[Z] := 'testing';
  10.   ShowMessage(Something[Z]);
  11. end;

By using eye-checking, we are sure the code above has out of bound issue. But the compiler fails to detect it. So turning range check on isn't always able to detect out of bound issue on compile time.

No range checking directive will do the same as {$R-} except there will be no warning messages during compile.

Don't forget you still may have (-Cr) in the project options.

Ian123

  • New Member
  • *
  • Posts: 28
..By using eye-checking, we are sure the code above has out of bound issue. But the compiler fails to detect it. So turning range check on isn't always able to detect out of bound issue on compile time.

True but that's why it adds the extra bounds-checking code to the executable, which is awesome imo.
Windows 10 64 bit *  Lazarus 1.8.0RC1 * i386-win32-win32/win64

Handoko

  • Hero Member
  • *****
  • Posts: 3851
  • My goal: build my own game engine using Lazarus
Sorry off topic.

I ever learned GW-BASIC, QBasic, Turbo Basic but not Visual Basic. Now you have tried Lazarus, can you please tell me how you think Lazarus compare to Quick Visual Basic?

I'm just curious to know how BASIC evolves now. BASIC is my first try language.
« Last Edit: June 07, 2017, 04:38:30 pm by Handoko »

Ian123

  • New Member
  • *
  • Posts: 28
Although i have used visual basic 6 for quite a while, I am far from being an expert in the language and also I am only really starting out in Lazarus.
At first I thought that I was  never going to like Lazarus it seemed too different.
I didn't like having the whole of the unit's code displayed. In VB I always preferred the procedure view. That probably puts me in a minority among even vb people though.
Anyway thanks to the help you and others gave me on this thread I am a lot more comfortable now.
Also it was just a matter of acclimatising.
Now, I like the pascal language, if anything, it's a bit wordier than vb6, which itself is often criticised for being too verbose.
Pascal makes you do a lot more explicit conversions inttostr etc that vb6 handles for you. I like this though as it makes for safer code.
The syntactical differences are not that great really, I still forget to put in the semi colon a lot and I prefer vb's way of using the equals sign for both
assignment and equality but those are small things.

I have toyed with vb.net and despite the language similarities to vb6, somehow I feel more at home in Lazarus than vb.net.

The controls Lazarus and VB6 have in common seems to share the same names for their properties etc to a large extent and the syntax of the two languages is close enough
to make moving from one to the other fairly trivial from that point of view.

Also looking at the LFM file for a form, it's really similar the design part of a vb FRM file (see pic), the biggest difference being the use of pixels in Lazarus vs twips in vb which is a pretty
simple conversion.

I almost feel like I could write a convertor for my vb projects.

Then there's the facts that Lazarus is open source, under constant development and free as well!
Windows 10 64 bit *  Lazarus 1.8.0RC1 * i386-win32-win32/win64

 

TinyPortal © 2005-2018