558 lines
24 KiB
Plaintext
558 lines
24 KiB
Plaintext
@database beginner.guide
|
|
|
|
@Master beginner
|
|
|
|
@Width 75
|
|
|
|
|
|
This is the AmigaGuide® file beginner.guide, produced by Makeinfo-1.55 from
|
|
the input file beginner.
|
|
|
|
|
|
@NODE "main" "Memory Allocation"
|
|
@Next "FloatingPoint.guide/main"
|
|
@Prev "Exceptions.guide/main"
|
|
@Toc "Contents.guide/main"
|
|
|
|
Memory Allocation
|
|
*****************
|
|
|
|
When a program is running memory is being used in various different
|
|
ways. In order to use any memory it must first be @{fg shine }allocated@{fg text }, which is
|
|
simply a way of marking memory as being `in use'. This is to prevent the
|
|
same piece of memory being used for different data storage (e.g., by
|
|
different programs), and so helps prevent corruption of the data stored
|
|
there. There are two general ways in which memory can be allocated:
|
|
dynamically and statically.
|
|
|
|
|
|
@{" Static Allocation " Link "Static Allocation" }
|
|
@{" Deallocation of Memory " Link "Deallocation of Memory" }
|
|
@{" Dynamic Allocation " Link "Dynamic Allocation" }
|
|
@{" NEW and END Operators " Link "NEW and END Operators" }
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Static Allocation" "Static Allocation"
|
|
@Next "Deallocation of Memory"
|
|
@Toc "main"
|
|
|
|
Static Allocation
|
|
=================
|
|
|
|
@{fg shine }Statically@{fg text } allocated memory is memory allocated by the program for
|
|
variables and static data like string constants, lists and typed lists
|
|
(see @{"Static data" Link "Types.guide/Static data" }). Every variable in a program requires some memory in
|
|
which to store its value. Variables declared to be of type @{b }ARRAY@{ub }, @{b }LIST@{ub },
|
|
@{b }STRING@{ub } or any object require two lots of memory: one to hold the value of
|
|
the pointer and one to hold the large amount of data (e.g., the elements
|
|
in an @{b }ARRAY@{ub }). In fact, such declarations are merely @{b }PTR TO @{ub }@{fg shine }type@{fg text }@{b }@{ub }
|
|
declarations together with an initialisation of the pointer to the address
|
|
of some (statically) allocated memory to hold the data. The following
|
|
example shows very similar declarations, with the difference being that in
|
|
the second case (using @{b }PTR@{ub }) only memory to hold the pointer values is
|
|
allocated. The first case also allocates memory to hold the appropriate
|
|
size of array, object and E-string.
|
|
|
|
DEF a[20]:ARRAY, m:myobj, s[10]:STRING
|
|
|
|
DEF a:PTR TO CHAR, m:PTR TO myobj, s:PTR TO CHAR
|
|
|
|
The pointers in the second case are not initialised by the declaration
|
|
and, therefore, they are not valid pointers. This means that they should
|
|
not be dereferenced in any way, until they have been initialised to the
|
|
address of some allocated memory. This usually involves dynamic
|
|
allocation of memory (see @{"Dynamic Allocation" Link "Dynamic Allocation" }).
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Deallocation of Memory" "Deallocation of Memory"
|
|
@Next "Dynamic Allocation"
|
|
@Prev "Static Allocation"
|
|
@Toc "main"
|
|
|
|
Deallocation of Memory
|
|
======================
|
|
|
|
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 @{fg shine }deallocation@{fg text } 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[40]: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 @{b }fullname@{ub } returns the value of the local
|
|
variable @{b }full@{ub }, 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 @{b }fullname@{ub } is the
|
|
address of recently deallocated memory, so it is invalid to dereference it.
|
|
But the call to @{b }WriteF@{ub } does just that: it dereferences the result of
|
|
@{b }fullname@{ub } 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
|
|
@{"Dynamic Allocation" Link "Dynamic Allocation" }).
|
|
|
|
If you're still a bit sceptical that this really is a problem, try the
|
|
above @{b }fullname@{ub } procedure definition with either of these replacement @{b }main@{ub }
|
|
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.)
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Dynamic Allocation" "Dynamic Allocation"
|
|
@Next "NEW and END Operators"
|
|
@Prev "Deallocation of Memory"
|
|
@Toc "main"
|
|
|
|
Dynamic Allocation
|
|
==================
|
|
|
|
@{fg shine }Dynamically@{fg text } allocated memory is any memory that is not statically
|
|
allocated. To allocate memory dynamically you can use the @{b }List@{ub } and @{b }String@{ub }
|
|
functions, all flavours of @{b }New@{ub }, and the versatile @{b }NEW@{ub } operator. But
|
|
because the memory is dynamically allocated it must be explicitly
|
|
deallocated when no longer needed. In all the above cases, though, any
|
|
memory that is still allocated when the program terminates will be
|
|
deallocated automatically.
|
|
|
|
Another way to allocate memory dynamically is to use the Amiga system
|
|
functions based on @{b }AllocMem@{ub }. However, these functions require that the
|
|
memory allocated using them be deallocated (using functions like @{b }FreeMem@{ub })
|
|
before the program terminates, or else it will never be deallocated (not
|
|
until your machine is rebooted, anyway). It is safer, therefore, to try
|
|
to use the E functions for dynamic allocation whenever possible.
|
|
|
|
There are many reasons why you might want to use dynamic allocation,
|
|
and most of them involve initialisation of pointers. For example, the
|
|
declarations in the section about static allocation can be extended to
|
|
give initialisations for the pointers declared in the second @{b }DEF@{ub } line (see
|
|
@{"Static Allocation" Link "Static Allocation" }).
|
|
|
|
DEF a[20]:ARRAY, m:myobj, s[10]:STRING
|
|
|
|
DEF a:PTR TO CHAR, m:PTR TO myobj, s:PTR TO CHAR
|
|
a:=New(20)
|
|
m:=New(SIZEOF myobj)
|
|
s:=String(20)
|
|
|
|
These are initialisations to dynamically allocated memory, whereas the
|
|
first line of declarations initialise similar pointers to statically
|
|
allocated memory. If these sections of code were part of a procedure
|
|
then, since they would now be local variables, there would be one other,
|
|
significant difference: the dynamically allocated memory would not
|
|
automatically be deallocated when the procedure returns, whereas the
|
|
statically allocated memory would. This means that we can solve the
|
|
deallocation problem (see @{"Deallocation of Memory" Link "Deallocation of Memory" }).
|
|
|
|
/* This is the correct way of doing it */
|
|
PROC fullname(first, last)
|
|
DEF full
|
|
full:=String(40)
|
|
StrCopy(full, first)
|
|
StrAdd(full, ' ')
|
|
StrAdd(full, last)
|
|
ENDPROC full
|
|
|
|
PROC main()
|
|
DEF f, g
|
|
WriteF('Name is \\s\\n', fullname('Fred', 'Flintstone'))
|
|
f:=fullname('Fred', 'Flintstone')
|
|
g:=fullname('Barney', 'Rubble')
|
|
WriteF('Name is \\s\\n', f)
|
|
ENDPROC
|
|
|
|
The memory for the E-string pointed to by @{b }full@{ub } is now allocated
|
|
dynamically, using @{b }String@{ub }, and is not deallocated until the end of the
|
|
program. This means that it is quite valid to pass the value of @{b }full@{ub } as
|
|
the result of the procedure @{b }fullname@{ub }, and it is quite valid to dereference
|
|
the result by printing it using @{b }WriteF@{ub }. However, this has caused one last
|
|
problem: the memory is not deallocated until the end of the program, so is
|
|
potentially wasted since it could be used, for example, to hold the
|
|
results of subsequent calls. Of course, the memory can be deallocated
|
|
only when the data it stores is no longer required. The following
|
|
replacement @{b }main@{ub } procedure shows when you might want to deallocate the
|
|
E-string (using @{b }DisposeLink@{ub }).
|
|
|
|
PROC main()
|
|
DEF f, g
|
|
f:=fullname('Fred', 'Flintstone')
|
|
WriteF('Name is \\s, f points to $\\h\\n', f, f)
|
|
/* Try this with and without the next DisposeLink line */
|
|
DisposeLink(f)
|
|
g:=fullname('Barney', 'Rubble')
|
|
WriteF('Name is \\s, g points to $\\h\\n', g, g)
|
|
DisposeLink(g)
|
|
ENDPROC
|
|
|
|
If you run this with the @{b }DisposeLink(f)@{ub } line you'll probably find that
|
|
@{b }g@{ub } will be a pointer to the same memory as @{b }f@{ub }. This is because the call
|
|
to @{b }DisposeLink@{ub } has deallocated the memory pointed to by @{b }f@{ub }, so it can be
|
|
reused to store the E-string pointed to by @{b }g@{ub }. If you comment out (or
|
|
delete) the @{b }DisposeLink@{ub } line, then you will find that @{b }f@{ub } and @{b }g@{ub } always point
|
|
to different memory.
|
|
|
|
In some ways it is best to never do any deallocation, because of the
|
|
problems you can get into if you deallocate memory too early (i.e., before
|
|
you've finished with the data it contains). Of course, it is safe (but
|
|
temporarily wasteful) to do this with the E dynamic allocation functions,
|
|
but it is very wasteful (and wrong) to do this with the Amiga system
|
|
functions like @{b }AllocMem@{ub }.
|
|
|
|
Another benefit of using dynamic allocation is that the size of the
|
|
arrays, E-lists and E-strings that can be created can be the result of any
|
|
expression, so is not restricted to constant values. (Remember that the
|
|
size given on @{b }ARRAY@{ub }, @{b }LIST@{ub } and @{b }STRING@{ub } declarations must be a constant.)
|
|
This means that the @{b }fullname@{ub } procedure can be made more efficient and
|
|
allocate only the amount of memory it needs for the E-string it creates.
|
|
|
|
PROC fullname(first, last)
|
|
DEF full
|
|
/* The extra +1 is for the added space */
|
|
full:=String(StrLen(first)+StrLen(last)+1)
|
|
StrCopy(full, first)
|
|
StrAdd(full, ' ')
|
|
StrAdd(full, last)
|
|
ENDPROC full
|
|
|
|
However, it may be very complicated or inefficient to calculate the
|
|
correct size. In these cases, a quick, constant estimate might be better,
|
|
overall.
|
|
|
|
The various functions for allocating memory dynamically have
|
|
corresponding functions for deallocating that memory. The following table
|
|
shows some of the more common pairings.
|
|
|
|
Allocation Deallocation
|
|
------------------------------
|
|
New Dispose
|
|
NewR Dispose
|
|
List DisposeLink
|
|
String DisposeLink
|
|
NEW END
|
|
FastNew FastDispose
|
|
AllocMem FreeMem
|
|
AllocVec FreeVec
|
|
AllocDosObject FreeDosObject
|
|
|
|
@{b }NEW@{ub } and @{b }END@{ub } are versatile and powerful operators, discussed in the
|
|
following section. The functions beginning with @{b }Alloc-@{ub } are Amiga system
|
|
functions and are paired with similarly suffixed functions with a @{b }Free-@{ub }
|
|
prefix. See the `Rom Kernel Reference Manual' for more details.
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "NEW and END Operators" "NEW and END Operators"
|
|
@Prev "Dynamic Allocation"
|
|
@Toc "main"
|
|
|
|
@{b }NEW@{ub } and @{b }END@{ub } Operators
|
|
=====================
|
|
|
|
To help deal with dynamic allocation and deallocation of memory there
|
|
are two, powerful operators, @{b }NEW@{ub } and @{b }END@{ub }. The @{b }NEW@{ub } operator is very
|
|
versatile, and similar in operation to the @{b }New@{ub } family of built-in
|
|
functions (see @{"System support functions" Link "BuiltIns.guide/System support functions" }). The @{b }END@{ub } operator is the
|
|
deallocating complement of @{b }NEW@{ub } (so it is similar to the @{b }Dispose@{ub } family of
|
|
built-in functions). The major difference between @{b }NEW@{ub } and the various
|
|
flavours of @{b }New@{ub } is that @{b }NEW@{ub } allocates memory based on the types of its
|
|
arguments.
|
|
|
|
|
|
@{" Object and simple typed allocation " Link "Object and simple typed allocation" }
|
|
@{" Array allocation " Link "Array allocation" }
|
|
@{" List and typed list allocation " Link "List and typed list allocation" }
|
|
@{" OOP object allocation " Link "OOP object allocation" }
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Object and simple typed allocation" "Object and simple typed allocation"
|
|
@Next "Array allocation"
|
|
@Toc "NEW and END Operators"
|
|
|
|
Object and simple typed allocation
|
|
----------------------------------
|
|
|
|
The following sections of code are roughly equivalent and serve to show
|
|
the function of @{b }NEW@{ub }, and how it is closely related to @{b }NewR@{ub }. (The @{fg shine }type@{fg text }
|
|
can be any object or simple type.)
|
|
|
|
DEF p:PTR TO @{fg shine }type@{fg text }
|
|
NEW p
|
|
|
|
DEF p:PTR TO @{fg shine }type@{fg text }
|
|
p:=NewR(SIZEOF @{fg shine }type@{fg text })
|
|
|
|
Notice that the use of @{b }NEW@{ub } is not like a function call, as there are no
|
|
parentheses around the parameter @{b }p@{ub }. This is because @{b }NEW@{ub } is an operator
|
|
rather than a function. It works differently from a function, since it
|
|
also needs to know the types of its arguments. This means that the
|
|
declaration of @{b }p@{ub } is very important, since it governs how much memory is
|
|
allocated by @{b }NEW@{ub }. The version using @{b }NewR@{ub } explicitly gives the amount of
|
|
memory to be allocated (using the @{b }SIZEOF@{ub } operator), so in this case the
|
|
declared type of @{b }p@{ub } is not so important for correct allocation.
|
|
|
|
The next example shows how @{b }NEW@{ub } can be used to initialise several
|
|
pointers at once. The second section of code is roughly equivalent, but
|
|
uses @{b }NewR@{ub }. (Remember that the default type of a variable is @{b }LONG@{ub }, which
|
|
is actually @{b }PTR TO CHAR@{ub }.)
|
|
|
|
DEF p:PTR TO LONG, q:PTR TO myobj, r
|
|
NEW p, q, r
|
|
|
|
DEF p:PTR TO LONG, q:PTR TO myobj, r
|
|
p:=NewR(SIZEOF LONG)
|
|
q:=NewR(SIZEOF myobj)
|
|
r:=NewR(SIZEOF CHAR)
|
|
|
|
These first two examples have shown the statement form of @{b }NEW@{ub }. There
|
|
is also an expression form, which has one parameter and returns the
|
|
address of the newly allocated memory as well as initialising the argument
|
|
pointer to this address.
|
|
|
|
DEF p:PTR TO myobj, q:PTR TO myobj
|
|
q:=NEW p
|
|
|
|
DEF p:PTR TO myobj, q:PTR TO myobj
|
|
q:=(p:=NewR(SIZEOF @{fg shine }type@{fg text }))
|
|
|
|
This may not seem desperately useful, but it's also the way that @{b }NEW@{ub } is
|
|
used to allocate copies of lists and typed lists (see
|
|
@{"List and typed list allocation" Link "List and typed list allocation" }).
|
|
|
|
To deallocate memory allocated using @{b }NEW@{ub } you use the @{b }END@{ub } statement with
|
|
the pointers that you want to deallocate. To work properly, @{b }END@{ub } requires
|
|
that the type of each pointer matches the type used when it was allocated
|
|
with @{b }NEW@{ub }. Failure to do this will result in an incorrect amount of memory
|
|
being deallocated, and this can cause many subtle problems in a program.
|
|
You must also be careful not to deallocate the same memory twice, and to
|
|
this end the pointers given to @{b }END@{ub } are re-initialised to @{b }NIL@{ub } after the
|
|
memory they point to is deallocated (it is quite safe to use @{b }END@{ub } with a
|
|
pointer which is @{b }NIL@{ub }). This does not catch all problems, however, since
|
|
more than one pointer can point to the same piece of memory, as shown in
|
|
the example below.
|
|
|
|
DEF p:PTR TO LONG, q:PTR TO LONG
|
|
q:=NEW p
|
|
p[]:=-24
|
|
q[]:=613
|
|
END p
|
|
/* p is now NIL, but q is now invalid but not NIL */
|
|
|
|
The first assignment initialises @{b }q@{ub } to be the same as @{b }p@{ub } (which is
|
|
initialised by @{b }NEW@{ub }). @{i }Both@{ui } the next two assignments change the value
|
|
pointed to by @{i }both@{ui } @{b }p@{ub } and @{b }q@{ub }. The memory allocated to store this value is
|
|
then deallocated, using @{b }END@{ub }, and this also sets @{b }p@{ub } to @{b }NIL@{ub }. However, the
|
|
address stored in @{b }q@{ub } is not altered, and still points to the memory that
|
|
has just been deallocated. This means that @{b }q@{ub } now has a plausible, but
|
|
invalid, pointer value. The only thing that can safely be done with @{b }q@{ub } is
|
|
re-initialise it. One of the @{i }worst@{ui } things that could be done is to use it
|
|
with @{b }END@{ub }, which would deallocate the same memory again, and potentially
|
|
crash your machine. So, in summary, don't deallocate the same pointer
|
|
value more than once, and keep track of which variables point to the same
|
|
memory as others.
|
|
|
|
Just as a use of @{b }NEW@{ub } has a simple (but rough) equivalent using @{b }NewR@{ub },
|
|
@{b }END@{ub } has an equivalent using @{b }Dispose@{ub }, as shown by the following
|
|
sections of code.
|
|
|
|
END p
|
|
|
|
IF p
|
|
Dispose(p)
|
|
p:=NIL
|
|
ENDIF
|
|
|
|
In fact, it's a tiny bit more complicated than that, since OOP objects are
|
|
allocated and deallocated using @{b }NEW@{ub } and @{b }END@{ub } (see @{"Object Oriented E" Link "OOE.guide/main" }).
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "Array allocation" "Array allocation"
|
|
@Next "List and typed list allocation"
|
|
@Prev "Object and simple typed allocation"
|
|
@Toc "NEW and END Operators"
|
|
|
|
Array allocation
|
|
----------------
|
|
|
|
Arrays can also be allocated using @{b }NEW@{ub }, and this works in a very
|
|
similar way to that outlined in the previous section. The difference is
|
|
that the size of the array must also be supplied, in both the use of @{b }NEW@{ub }
|
|
and @{b }END@{ub }. Of course, the size supplied to @{b }END@{ub } must be the same as the size
|
|
supplied to the appropriate use of @{b }NEW@{ub }. All this extra effort also gains
|
|
you the ability to create an array of a size which is not a constant
|
|
(unlike variables of type @{b }ARRAY@{ub }). This means that the size supplied to
|
|
@{b }NEW@{ub } and @{b }END@{ub } can be the result of an arbitrary expression.
|
|
|
|
DEF a:PTR TO LONG, b:PTR TO myobj, s
|
|
NEW a[10] /* A dynamic array of LONG */
|
|
s:=my_random(20)
|
|
NEW b[s] /* A dynamic array of myobj */
|
|
/* ...some other code... */
|
|
END a[10], b[s]
|
|
|
|
The @{b }my_random@{ub } function stands for some arbitrary calculation, to show that
|
|
@{b }s@{ub } does not have to be a constant. This form of @{b }NEW@{ub } can also be used as an
|
|
expression, as before.
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "List and typed list allocation" "List and typed list allocation"
|
|
@Next "OOP object allocation"
|
|
@Prev "Array allocation"
|
|
@Toc "NEW and END Operators"
|
|
|
|
List and typed list allocation
|
|
------------------------------
|
|
|
|
Lists and typed lists are usually static data, but @{b }NEW@{ub } can be used to
|
|
create dynamically allocated versions. This form of @{b }NEW@{ub } can be used only
|
|
as an expression, and it takes the list (or typed list) as its argument
|
|
and returns the address of the dynamically allocated copy of the list.
|
|
Deallocation of the memory allocated in this way is a bit more complicated
|
|
than before, but you can, of course, let it be deallocated automatically
|
|
at the end of the program.
|
|
|
|
The following example shows how simple it is to use @{b }NEW@{ub } to cure the
|
|
static data problem described previously (see @{"Static data" Link "Types.guide/Static data" }). The
|
|
difference from the original, incorrect program is very subtle.
|
|
|
|
PROC main()
|
|
DEF i, a[10]:ARRAY OF LONG, p:PTR TO LONG
|
|
FOR i:=0 TO 9
|
|
a[i]:=NEW [1, i, i*i]
|
|
/* a[i] is now dynamically allocated */
|
|
ENDFOR
|
|
FOR i:=0 TO 9
|
|
p:=a[i]
|
|
WriteF('a[\\d] is an array at address \\d\\n', i, p)
|
|
WriteF(' and the second element is \\d\\n', p[1])
|
|
ENDFOR
|
|
ENDPROC
|
|
|
|
The minor alteration is to prefix the list with @{b }NEW@{ub }, thereby making the
|
|
list dynamic. This means that each @{b }a[i]@{ub } is now a different list, rather
|
|
than the same, static list of the original version of the program.
|
|
|
|
Typed lists are allocated in a similar way, and the following example
|
|
also shows how to deallocate this memory. Basically, you need to know how
|
|
long the new array is (i.e., how many elements there are), since a typed
|
|
list is really just an initialised array. You can then deallocate it like
|
|
a normal array, remembering to use an appropriately typed pointer.
|
|
Object-typed lists are restricted (when used with @{b }NEW@{ub }) to an array of at
|
|
most one object, so is useful only for allocating an initialised object
|
|
(not really an array). Notice how, in the following code, the pointer @{b }q@{ub }
|
|
can be treated both as an object and as an array of one object (see
|
|
@{"Element selection and element types" Link "Types.guide/Element selection and element types" }).
|
|
|
|
OBJECT myobj
|
|
x:INT, y:LONG, z:INT
|
|
ENDOBJECT
|
|
|
|
PROC main()
|
|
DEF p:PTR TO INT, q:PTR TO myobj
|
|
p:=NEW [1, 9, 3, 7, 6]:INT
|
|
q:=NEW [1, 2]:myobj
|
|
WriteF('Last element in array p is \\d\\n', p[4])
|
|
WriteF('Object q is x=\\d, y=\\d, z=\\d\\n',
|
|
q.x, q.y, q.z)
|
|
WriteF('Array q is q[0].x=\\d, q[0].y=\\d, q[0].z=\\d\\n',
|
|
q[].x, q[].y, q[].z)
|
|
END p[5], q
|
|
ENDPROC
|
|
|
|
The dynamically allocated version of an object-typed list differs from the
|
|
static version in another way: it always has memory allocated for a whole
|
|
number of objects, so a partially initialised object is padded with zero
|
|
elements. The static version does not allocate this extra padding, so you
|
|
must be careful not to access any element beyond those mentioned in the
|
|
list.
|
|
|
|
The deallocation of @{b }NEW@{ub } copies of normal lists can, as ever, be left to
|
|
be done automatically at the end of the program. If you want to
|
|
deallocate them before this time you must use the function
|
|
@{b }FastDisposeList@{ub }, passing the address of the list as the only argument.
|
|
You @{i }must@{ui } not use @{b }END@{ub } or any other method of deallocation. @{b }FastDisposeList@{ub }
|
|
is the only safe way of deallocating lists allocated using @{b }NEW@{ub }.
|
|
|
|
|
|
@ENDNODE
|
|
|
|
@NODE "OOP object allocation" "OOP object allocation"
|
|
@Prev "List and typed list allocation"
|
|
@Toc "NEW and END Operators"
|
|
|
|
OOP object allocation
|
|
---------------------
|
|
|
|
Currently, the only way to create OOP objects in E is to use @{b }NEW@{ub } and
|
|
the only safe way to destroy them is to use @{b }END@{ub }. This is probably the
|
|
most common use of @{b }NEW@{ub } and @{b }END@{ub } and is described in detail later (see
|
|
@{"Objects in E" Link "OOE.guide/Objects in E" }).
|
|
|
|
|
|
@ENDNODE
|
|
|