Dynamic Memory Allocation and Arrays

Q. How do I declare an array with a dynamic number of elements?

A. The short answer is, you cannot dynamically size an array at runtime. But you can dynamically allocate memory for an array. The %ALLOC and %REALLOC functions, along with the DEALLOC operation, can assign storage to an array that was not allocated to the array when the program started.

The %ALLOC function allocates a chunk of storage (up to 16MB), and returns a pointer to that storage. %REALLOC changes the size of the storage associated with a pointer. Finally, DEALLOC releases the storage associated with a pointer.

Here’s a code snippet that will dynamically allocate an array:

D Arrayptr        S               *   Inz(*Null)
D Array           S             15  5 Dim(32767)
D                                     Based(Arrayptr)
/Free
// Allocate memory for 16 elements
Arrayptr = %Alloc(%Size(Array) * 16);
... (You can now process up to 16 elements of the array.)
// Allocate memory for 32 elements
Arrayptr = %Realloc(Arrayptr: %Size(Array) * 32);
... (You can now process up to 32 elements.)
// Back to 16 elements
Arrayptr = %Realloc(Arrayptr: %Size(Array) * 16);
... (You can now process only 16 elements.)
// Release memory used by Arry
Dealloc(en) Arrayptr;
... (You may no longer process any elements in the array.)
/End-free

In this example, Array is defined with 32767 elements (the maximum allowed), but there is no storage allocated for it. The contents of Array are based upon the address in Arrayptr.

To allocate storage for Array, I use the %ALLOC function, specifying a number of bytes to allocate, 128 in this case (%Size(Array) * 16). Now I can process 16 elements in Array.

When I need more elements, I use the %REALLOC function to double the size to 256. The values of the existing 16 elements will remain in the array, but now I can process up to 32 elements.

If I want to back down the number of elements to 16 again, I can again use %REALLOC, but with 128 bytes this time.

Finally, I use the DEALLOC operation to free the memory allocated to Arrayptr. The (n) extender sets the pointer to a *Null address. Always remember to deallocate any memory that you allocate. Setting on LR will not release any memory you’ve allocated, although you will lose the pointer to the memory; this is commonly known as a “memory leak.” The memory will be automatically released when the activation group or the job ends.

The %ALLOC function will not initialize any new memory that it allocates. The %REALLOC function allocate new storage (releasing the old storage) and copy data from the old storage; the new storage following the copied data will not be initialized. If the %REALLOC size is less than the old memory, the data will be truncated. To make a long story short, don’t try to access any of the new storage (i.e., the new array elements) until you’ve initialized the data.

Storage management errors may occur with these operations. You can capture them with the Monitor operation, or with the (e) extender for DEALLOC. %Status 0425 will occur if there is insufficient storage for %REALLOC, or if %ALLOC tries to allocate more than 16MB; %Status 0426 will occur for any other storage management error.

It’s important to remember that, while you are dynamically allocating memory for the array, you are not dynamically changing the number of elements in the array. The %ELEM function will still indicate 32767 elements throughout the entire process.

The RPG IV Reference, in its typically understated manner, says, “Misuse of heap storage can cause problems.”