Use variables of type
integer pointer(2)
to store pointers (can be shared in common blocks).
call alloc(pointer, n)
where n
is the number of bytes to
allocate to allocate a block of memory. The pointer is stored in
pointer
.
Use the functions complexsize(), realsize(),
integersize()
and logicalsize()
from
lib/utilities/platform.F
as prefactors to create
portable code, e.g. n=realsize()*len
to allocate a
double precision array that can hold len numbers.
call resize(pointer, m)
to resize the array to the new size in bytes
m
, data already present in the array is copied into
the new location.
If pointer
is set to zero prior to calling
realloc
, this routine can also be used instead of
alloc. This is useful when an array is to grow in a loop along
with the arrival of data.
Data is stored in an array via
call set****(value, pointer, index)
where ****
stands for character, short, long,
lli, float, double, scpx, dcpx
.
These can be used to access elements of arrays that store
integer*1
(bytes), integer*2
(words),
integer*4
, integer*8
,
real*4
, real*8
(double precision),
complex*8
(single complex), and
complex*16
(double complex) variables.
The set****
call can be envisaged as
corresponding to the Fortran statement
array(index) = value
For most types, the elements in a dynamic array are accessed
via
variable = get****(pointer, index)
where **** are as before and the statement is equivalent to
Fortran's
variable = array(index)
.
Note that the getlli
and the
get?cpx
calls for long long int
and
complex numbers cannot be called this way.
Instead, they need to be called procedurally, i.e. the above
Fortran call corresponds to
call getdcpx(variable, pointer,
index)
This has become necessary due to a shortcoming in the specification of the Fortran standard which allowed different Fortran compilers to implement passing of complex function results differently.
All other functions get**** can be used just like any
ordinary Fortran function, e.g. in a debug statement of the
form
write(0,'(e16.8)')(getdouble(buffer,
i),i=1,buflen)
Note also that with setlli you can actually store pointer variables, so you can create arrays of arrays (i.e. two-dimensional arrays). This was done with arrays of arrays of double precision for in source/opfuncs/linint.F. Have a look to see how it was done.
One of the advantages of using dynamic memory allocation is that it allows to free memory, so that more memory is available for other sections of the program.
Memory allocated can be deallocated by calling either of
call dealloc(pointer)
call resize(pointer, 0)
Random access to data is almost as fast with the
dynaccess
library as on Fortran 77 arrays. However,
sequential data access is much faster with Fortran 77
arrays.
In order to achieve better performance for sequential access of
large blocks of data, blockwise operations on dynamical arrays
can be performed by
call setblock(buffer, pointer, offset,
length)
call getblock(buffer, pointer, offset,
length)
where buffer(*)
is a Fortran array of any type
that is used to store data to or read it from the dynamic array,
integer pointer(2)
is a Fortran pointer placeholder
which has been initialised with alloc or resize,
offset
is the offset in bytes from the
start of the memory designated by pointer
and
length
is the length in bytes of the
data block to copy.
Use the functions complexsize(), realsize(),
integersize()
and logicalsize()
to translate
array indices and lengths into byte offsets and lengths.
Note that copying from the beginning of the memory block is
done by using offset=0
, i.e. we are counting from
zero instead of counting from one, which would have been the
usual Fortran way (and the way it is done in all other get****
and set**** routines to make them less confusing to use from
Fortran).
This convention is more natural when operating with
complexsize(), realsize()
etc. and hence has been
used for the ***block
subroutines.
The speed advantage of the ***block
subroutines
stems from the fact that large data sets are transferred in one
function call.
An array containing strings is an array of arrays containing
characters, which is a common problem in programming, so special
routines were devised for this case. Allocation:
call newstring(pointer, n)
where n
is the number of stings to be
stored.
Resizing is done via
call resizestring(pointer, n, o)
where n
is the new allocation size and
o
is the old one. Freeing all memory occupied by the
array of string pointers and the strings themselves is done with
either of
call freestring(pointer)
call releasestring(pointer, n)
Array elements (i.e. strings) are set with
call setstring(string, pointer,
index)
which corrsponds to
array(index) = string
and read out with
call getstring(string, pointer,
index)
which corresponds to
string = array(index)
Note that the latter is not a function but a procedure because
we are copying an array of characters into the character*(*)
variable string
.
This routine is fail-safe, i.e. if the string
variable is shorter than what was stored in the array element,
only the first few characters are retrieved; if it is longer, it
is filled up with white blanks (as is the Fortran way).
Note that setstring
allocates enough memory to
hold the string string
, i.e. calling it via
call setstring(string(start:end), pointer,
index)
will save memory if you only need a part of a string.
These techniques were used in source/opfuncs/linint.F. Have a look to see how it was done.