Dynamic Memory Allocation and Arrays

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

A. The short answer is, until Release 7.4 (7.3 with latest PTFs), you cannot dynamically size an array at runtime.

At Release 7.4, you can  define varying-dimension arrays:

Dcl-s Array Char(15) Dim(*AUTO:32767);

or

Dcl-s Array Char(15) Dim(*VAR:32767);

The second parameter of the DIM keyword indicates the maximum number of elements in the array.

The initial dimension of the array is zero. To change the dimension, assign a value to the %ELEM built-in function:

%Elem(Array) = 36;

If you define an array with DIM(*AUTO), the dimension increases if you assign an %Elem value greater than the current number of elements. Or you can assign a value to an element greater than the current dimension. For example if the current dimension is 36, the following statement would increase the dimension to 40:

Array(40) = 'XYZ';

In this case, elements 37-39 would be initialized to their INZ default values (blanks in this case).

You can also increase the dimension of a DIM(*AUTO) array by one if you assign a value using the *NEXT keyword. For example, if Array has a current dimension of 40 the following statement would increment its dimension to 41:

Array(*Next) = 'ABC';

The current dimension of the array is automatically increased by one for each assignment to Array(*Next).

Learn more about varying-dimension arrays and other ILE RPG Release 7.4 features in the “ILE RPG in Easy Bytes” series,” one of several online self-study courses available at my.enskill.com.

Before Release 7.4…

Before Release 7.4, 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.”