Hi Given a pointer p, dispose(p) does not set p to nil.
This program,
program tst; type ptr = ^string; var p : ptr; begin p := new( ptr, 20 ); p^ := "abc"; writeln( p^ ); dispose( p ); p := nil; if p = nil then writeln("OK"); dispose( p ); writeln( p^ ); end.
as is gives you
abc OK segmentation fault
The seg fault is from the last writeln. If you comment out the line "p := nil", the second "dispose" causes a core dump. If you comment out both "p := nil" and the second "dispose" you get
abc abc
and that's also an error.
I do not know what side effects you might get if you caused "dispose" to set the pointer to nil.
Russ
Russell Whitaker wrote:
Given a pointer p, dispose(p) does not set p to nil.
This program,
program tst; type ptr = ^string; var p : ptr; begin p := new( ptr, 20 ); p^ := "abc"; writeln( p^ ); dispose( p ); p := nil; if p = nil then writeln("OK"); dispose( p ); writeln( p^ ); end.
as is gives you
abc OK segmentation fault
The seg fault is from the last writeln. If you comment out the line "p := nil", the second "dispose" causes a core dump. If you comment out both "p := nil" and the second "dispose" you get
abc abc
and that's also an error.
For this very reason, I always use allocation/deallocation wrappers that do something like
procedure mydispose( var p: pointer); begin if p <> nil then begin dispose(p); p:=nil end end;
I do not know what side effects you might get if you caused "dispose" to set the pointer to nil.
Neither do I know whether the standard allows it. But it's quite a useful feature.
Regards,
Adriaan van Os
Russell Whitaker a écrit:
Hi Given a pointer p, dispose(p) does not set p to nil.
I do not know what side effects you might get if you caused "dispose" to set the pointer to nil.
This is not contained in the standards: it is said "after dispose the pointer is undefined", not nil. Many people complain in various pascal-like forums (delphi etc). In fact this is a matter of programming style, whether you rely on a pointer being nil to decide whether it points to a valid address. Doing that is a source of bugs: you can affect an other pointer to the same block or part of it. This new pointer will not be set to nil when disposing of the first. If you rely blindly on tests to nil you will be in trouble. This is probably the origin of this behaviour in standard pascal. If you know what you are doing, never affecting pointers, you can use wrappers like the one suggested by Adriaan. Making this mandatory in the syntax would induce other people to suppose wrongly that they can do the nil test in any case. The only way out is a much more complex "managed" language which keeps track of all the pointers in use, to be able to set to nil _all_ pointers which point somewhere into a disposed of block. This would have a strong penalty in execution time.
Maurice
Russell Whitaker wrote:
Hi Given a pointer p, dispose(p) does not set p to nil.
This program,
program tst; type ptr = ^string; var p : ptr; begin p := new( ptr, 20 ); p^ := "abc"; writeln( p^ ); dispose( p ); p := nil; if p = nil then writeln("OK"); dispose( p ); writeln( p^ ); end.
as is gives you
abc OK segmentation fault
The seg fault is from the last writeln. If you comment out the line "p := nil", the second "dispose" causes a core dump. If you comment out both "p := nil" and the second "dispose" you get
abc abc
and that's also an error.
I do not know what side effects you might get if you caused "dispose" to set the pointer to nil.
Russ
In standard Pascal, dispose does not set p to nil, that's up to you. Calling dispose twice for the same variable will certainly cause a system fault. Calling dispose with nil is also an error.
What did you expect to happen?
On Mon, 10 Dec 2007, Scott Moore wrote:
Russell Whitaker wrote:
Hi Given a pointer p, dispose(p) does not set p to nil.
This program,
program tst; type ptr = ^string; var p : ptr; begin p := new( ptr, 20 ); p^ := "abc"; writeln( p^ ); dispose( p ); p := nil; if p = nil then writeln("OK"); dispose( p ); writeln( p^ ); end.
as is gives you
abc OK segmentation fault
The seg fault is from the last writeln. If you comment out the line "p := nil", the second "dispose" causes a core dump. If you comment out both "p := nil" and the second "dispose" you get
abc abc
and that's also an error.
I do not know what side effects you might get if you caused "dispose" to set the pointer to nil.
Russ
In standard Pascal, dispose does not set p to nil, that's up to you. Calling dispose twice for the same variable will certainly cause a system fault. Calling dispose with nil is also an error.
What did you expect to happen?
There probably wouldn't be anything wrong with GPC automatically setting pointers to nil when disposing them, but that would be non-standard and I suspect that most people (myself included) would do it themselves anyway (for portability's sake) unless the pointer was going to be immediately reassigned.
--------------------------| John L. Ries | Salford Systems | Phone: (619)543-8880 x107 | or (435)867-8885 | --------------------------|
John L. Ries wrote:
There probably wouldn't be anything wrong with GPC automatically setting pointers to nil when disposing them, but that would be non-standard and I suspect that most people (myself included) would do it themselves anyway (for portability's sake) unless the pointer was going to be immediately reassigned.
No, that's perfectly ok with the standard. It simply specifies that the value is undefined, which means set to nil is as good as anything else.
The reason it is not standard to set the value to nil is likely that it is false security. You set the pointer used in the dispose call to nil, but you could have any number of copies running around. Wirth was simply reminding you that it's up to you to manage your pointers in this way.
Scott Moore
At 9:44 -0800 10/12/07, Scott Moore wrote:
John L. Ries wrote:
There probably wouldn't be anything wrong with GPC automatically setting pointers to nil when disposing them, but that would be non-standard and I suspect that most people (myself included) would do it themselves anyway (for portability's sake) unless the pointer was going to be immediately reassigned.
No, that's perfectly ok with the standard. It simply specifies that the value is undefined, which means set to nil is as good as anything else.
I do not believe that is correct.
I'm pretty sure the semantics for dispose are that it takes a value parameter, and so has no ability to modify the pointer.
As much as I might prefer the definition to be that the pointer is set to nil, and like Adriaan, my preference is for wrapper functions that are nil-safe and idempotent, changing the behaviour of dispose could make correct code fail.
For example, imagine this pseudocode:
p = New pointer. AddToListOfPointers( list, p ); Dispose( p ); RemoveFromListOfPoitners( list, p );
That is perfectly legal code, but would fail if dispose became a by reference, set to nil procedure.
As Adriaan described, if you want that facility, simply write your own wrappers.
My own wrappers included:
* nil-safe * nil-setting * leak checking code * memory trashing
Enjoy, Peter.
Peter N Lewis wrote:
At 9:44 -0800 10/12/07, Scott Moore wrote:
John L. Ries wrote:
There probably wouldn't be anything wrong with GPC automatically setting pointers to nil when disposing them, but that would be non-standard and I suspect that most people (myself included) would do it themselves anyway (for portability's sake) unless the pointer was going to be immediately reassigned.
No, that's perfectly ok with the standard. It simply specifies that the value is undefined, which means set to nil is as good as anything else.
I do not believe that is correct.
I'm pretty sure the semantics for dispose are that it takes a value parameter, and so has no ability to modify the pointer.
As much as I might prefer the definition to be that the pointer is set to nil, and like Adriaan, my preference is for wrapper functions that are nil-safe and idempotent, changing the behaviour of dispose could make correct code fail.
For example, imagine this pseudocode:
p = New pointer. AddToListOfPointers( list, p ); Dispose( p ); RemoveFromListOfPoitners( list, p );
That is perfectly legal code, but would fail if dispose became a by reference, set to nil procedure.
As Adriaan described, if you want that facility, simply write your own wrappers.
My own wrappers included:
- nil-safe
- nil-setting
- leak checking code
- memory trashing
Enjoy, Peter.
Well, you could always "discover" that the expression qualifies as variable reference. I think the only thing that having a pointer as an expression gives over variable references is the ability to include function returns, since pointers are inadmissible to any operator save those that don't deliver a pointer in any case (p = x, p <> x).
For your second point, I see what you are getting at, but you might want to look closely at the standard there:
Dispose( p ); RemoveFromListOfPoitners( list, p );
ie,. what is the value of p? Your code implies p remains the same.
6.6.5.3 Dynamic allocation procedures
new(p) shall create a new variable that is totally-undefined, shall create a new identifying-value of the pointer-type associated with p, that identifies the new variable, and shall attribute this identifying-value to the variable denoted by the variable-access p . The created variable shall possess the type that is the domain-type of the pointer-type possessed by p.
...
dispose(q) shall remove the identifying-value denoted by the expression q from the pointer-type of q . It shall be an error if the identifying-value had been created using the form new(p,c l , . . .,cn).
...
NOTE -- The removal of an identifying-value from the pointer-type to which it belongs renders the identified-variable inaccessible (see 6.5.4) and makes undefined all variables and functions that have that value attributed (see 6.6.3.2 and 6.8.2.2).
ie., dispose indeed takes a value parameter, which it is NOT supposed to change, but here the standard seems to say that it is, indeed changed, to undefined.
This says at the least "don't count on the value", but it can be interpreted as room to implement a recycling system that finds and invalidates pointers, even non-obvious ones. In that case, the code shown would not work.
Either way, we agree it's a bad idea.
Scott Moore
On Mon, 10 Dec 2007, Scott Moore wrote:
[..]
What did you expect to happen?
I expected "dispose" to undo what "new" does. Could not find a comment in gpc's source that states otherwise.
At present,
new(p) 1. allocates a block of memory from the heap, the size is the size that p wants to point to. 2. returns the address of that memory block.
dispose(p) 1. marks the block of memory that p points to as free. (I say "marks" because the block doesn't disapear)
Thus, this works, but shouldn't:
writeln( p^ ); dispose( p ); writeln( p^ );
If you dig into gpc's heap.pas you will find code that is the equivalent of:
if p <> nil then dispose(p);
What I'd like to see is the equivalent of:
if p <> nil then begin dispose(p); p := nil; end else writeln( stderr,"WARN: illegal use of dispose");
In places the specs are hard to follow, but I think the specs would allow this.
Russ