When memory is allocated it is, conceptually, marked as being `in use'. This means that this piece of memory cannot be allocated again, so a different piece will be allocated (if any is available) when the program wants to allocate some more. In this way, variables are allocated different pieces of memory, and so their values can be distinct. But there is only a certain amount of memory available, and if it could not be marked as `not in use' again it would soon run out (and the program would come to a nasty end). This is what deallocation does: it marks previously allocated memory as being `not in use' and so makes it available for allocation again. However, memory should be deallocated only when it is actually no longer in use, and this is where things get a bit complicated.
Memory is such a vital resource in every computer that it is important to use as little of it as necessary and to deallocate it whenever possible. This is why a programming language like E handles most of the memory allocation for variables. The memory allocated for variables can be automatically deallocated when it is no longer possible for the program to use that variable. However, this automatic deallocation is not useful for global variables, since they can be used from any procedure and so can be deallocated only when the program terminates. A procedure's local variables, on the other hand, are allocated when the procedure is called but cannot be used after the procedure returns. They can, therefore, be deallocated when the procedure returns.
Pointers, as always, can cause big problems. The following example shows why you need to be careful when using pointers as the return value of a procedure.
/* This is an example of what *NOT* to do */ PROC fullname(first, last) DEF full:STRING StrCopy(full, first) StrAdd(full, ' ') StrAdd(full, last) ENDPROC full PROC main() WriteF('Name is \s\n', fullname('Fred', 'Flintstone')) ENDPROC
On first sight this seems fine, and, in fact, it may even work correctly if you run it once or twice (but be careful: it could crash your machine).
The problem is that the procedure
fullname returns the value of the local variable
full, which is a pointer to some statically allocated memory for the E-string and this memory will be deallocated when the procedure returns.
This means that the return value of any call to
fullname is the address of recently deallocated memory, so it is invalid to dereference it.
But the call to
WriteF does just that: it dereferences the result of
fullname in order to print the E-string it points to.
This is a very common problem, because it is such an easy thing to do.
The fact that it may, on many occasions, appear to work makes it much harder to find, too.
The solution, in this case, is to use dynamic allocation (see 14.3 Dynamic Allocation).
If you're still a bit sceptical that this really is a problem, try the above
fullname procedure definition with either of these replacement
main procedures, but be aware, again, that each one has the potential to crash your machine.
/* This might not print the correct string */ PROC main() DEF f f:=fullname('Fred', 'Flintstone') WriteF('Name is \s\n', f) ENDPROC /* This will definitely print g instead of f */ PROC main() DEF f, g f:=fullname('Fred', 'Flintstone') g:=fullname('Barney', 'Rubble') WriteF('Name is \s\n', f) ENDPROC
(The reason why things go wrong is outlined above, but the reasons why each prints what it does is beyond the scope of this Guide.)
Go to the Next or Previous section, the Detailed Contents, or the Amiga E Encyclopedia.