Libraries and Packages: The VOS Interface

2.2 Memory Allocation -- memio

Memory may be dynamically
allocated within an SPP application. The memory is referenced by a pointer, an int value containing the memory location of the first element of the buffer. The allocated memory may then be accessed as if it were a statically allocated array. The advantages to allocating memory dynamically are to reduce the size of compiled code and to allocate arrays whose size is not known at compile time. The pointer is used in subsequent procedure calls to refer to the allocated memory. The Mem[] construct is used to access the data. When passed to a procedure, the data are treated simply as an SPP array.

Pointers are indices into (one indexed) Fortran arrays. A pointer to an object of one data type will in general have a different value than a pointer to an object of a different data type, even if the objects are stored at the same physical address. Pointers have strict alignment requirements, and it is not always possible to coerce the type of a pointer. For this reason, the pointers returned by malloc() and salloc() are always aligned for all data types, regardless of the data type requested.

There are two types of dynamically allocated memory: stack and heap. They are treated identically in terms of dealing with the allocated data, but the mechanics of the allocation differ slightly.

malloc and relatives

Heap memory is used for arbitrarily large buffers and the resulting pointers may be stored and passed to calling and called procedures.

Table 2.5: Heap Memory Allocation Procedures.

Many VOS library procedures return a pointer allocated by malloc(), the imio procedures, for example. Be sure to free the memory by using the mfree() procedure. Note that the mfree() procedure in addition to the allocation procedures requires the data type of the allocated memory as an argument. These data types are passed as predefined parameter constants, defined by the system, for example, TY_INT, TY_REAL, etc.

Table 2.6: Memory Allocation Parameter Data Types.

Memory allocated explicitly with malloc() should be freed after use by mfree(). Pointers allocated implicitly, by immap(), etc., for example, should not be freed explicitly. They will be freed by the appropriate close procedure such as imunmap(). The realloc() procedure changes the size of a previously allocated buffer, copying the contents of the buffer if necessary. This is useful when allocating memory of unspecified size. For example, when reading from STDIN, you might allocate a data buffer initially with some default size. After reading all of the data you may wish to use realloc() to insure that the buffer is only as big as the amount of the data read. Note that realloc() will allocate new memory if the passed pointer is NULL, so it may be used in place of malloc(). This may be useful in a loop in which you need not use malloc() the first time you enter the loop. The only difference between malloc() and calloc() is that the latter sets all of the buffer values to zero, while the former retains the contents of the memory locations, which should be considered garbage. The following example illustrates allocating a block of memory using malloc() and calling a procedure to perform some operation on the values.

Note that the dostuff() procedure need not have nested loops if the operation is independent of column or row information. In fact, the vector operator (vops) procedures may be used for any dimensionality of arrays.

smark and salloc

Stack memory is useful for small buffers local to a procedure.

Table 2.7: Stack Memory Procedures.

The salloc() procedure allocates stack memory. This is a preallocated block of memory, a chunk of which may be used temporarily by a task. This differs from malloc() which allocates the memory at the time it is called. To use salloc(), a stack pointer must be referenced first using the smark() procedure. This marks the beginning of the block of memory to be referenced. It is not necessary (nor possible) to free the individual memory buffers allocated using salloc(). However, the stack pointer should be reset using sfree() at the end of the procedure. The memory pointer returned by salloc() should not be passed back to a calling procedure but may be passed down to a called procedure. Otherwise stack memory is used identically to heap memory allocated by malloc() or calloc(), see Example 2.5.

Data Structures

Dynamic memory is often used in creating and using data structures (see "Macro Definitions" on page 16 and "Data Structures" on page 18 for more details and additional examples). The structure is described by macro define statements declaring the components of the structure. These may be based on dynamically allocated memory, in which case the memory must be allocated before the structure is addressed, and the memory pointer passed as an argument to the structure element. Example 2.6 shows some code that may reside in an include file; it declares a structure consisting of integers and strings.

The strings (SPOOL_OUTPUT, for example) are in turn declared using dynamically allocated memory, the pointer being saved in another element of the structure. The elements are addressed with the Mem constructs. To use this structure, the memory must first be allocated using malloc() or calloc() with a data type of TY_STRUCT (see Example 2.7). The first line of the macro provides the number of elements to allocate. Elements of the structure are referenced name, with the pointer to the dynamically allocated memory passed as an argument to the macro.

There may also be substructures, pointed to by an element of the primary structure. Example 2.8 shows a substructure called from the gio structure defined in lib$gset.h.

Example 2.8 defines a structure for storing polyline attributes. GP_PLAP is a member of the top-level gio structure and PL_LTYPE for example is a member of the polyline substructure. These would be used in code as shown in Example 2.9.

A more complicated example (Example 2.10) illustrates a two-dimensional array in a substructure, again from gio. Note the use of two arguments to the macro, referred to as $1 and $2 in the definition.

Example 2.11 shows how the two-dimensional in the structure could be used. Note the two arguments to the macro GP_WCSPTR, one of which is itself a symbolic definition, GP_WCS, also part of the data structure. The structure defined in Example 2.10 is a fragment of the gio header file gset.h, included in the source example.

malloc and relatives
Table 2.5: - Heap Memory Allocation Procedures.
Table 2.6: - Memory Allocation Parameter Data Types.
smark and salloc
Table 2.7: - Stack Memory Procedures.
Data Structures

Generated with CERN WebMaker