The key to using these built-in functions and op-codes, and to accessing dynamic memory, is pointers -- basing pointers to be exact. In Integrated Language Environment (ILE) RPG, the variables used to access dynamic memory must be based on a pointer.
Dissecting the dynamic array program
In the example program, the array DynArr is used to access the dynamic memory so it is declared as based on pointer pDynArr. Pointer pDynArr is then used to store the return values (i.e., pointers) from the %Alloc and %Realloc built-in functions, which will be the address locations of the storage blocks in memory once they have been allocated.
Note: The pointer variable pDynArr is not explicitly declared in the code. This is perfectly legal. If you do not explicitly declare a pointer that is specified as the argument of the Based keyword, then the compiler declares it automatically declares it for you.
The DynArr array is also declared with the maximum number of elements (32,767). Because the RPG compiler adds runtime code to check for array index violations each time an array element is accessed (even based-on arrays), the number of elements specified on the d-spec must be at least as many as the program will ever need to allocate. Since the system does not allocate any memory for variables that are based on a pointer (remember, that's your job), I recommend dimensioning dynamic arrays with the maximum number of elements for maximum flexibility, as shown. However, you must be careful to maintain the current number allocated elements and ensure that your program does not try to access beyond this limit -- otherwise you will be accessing memory that is not allocated to your program (and perhaps corrupting memory that is allocated to another program).
The program starts by allocating enough storage for 100 elements of the array. To determine the number of bytes needed for this, we employ the %Size built-in function. By specifying just the array name as its argument, %Size will return the number of bytes needed to store one element of the array. Multiply this return value by 100 and we have the number of bytes needed to store 100 elements of the array.
Next, the storage is allocated using the %Alloc built-in function. This built-in function accepts as its parameter the number of bytes of storage you want allocated and returns a pointer to the first byte of the storage once it has allocated it, which we store in pointer pDynArr. Then, just to illustrate how easy it is to use the array, the program fills up the first 100 elements with the current system timestamp, and then displays the contents of one of those elements. Notice that you use the array as though it was an ordinary array, and in every other sense it is; the only difference is that you are responsible for allocating and deallocating the memory for it.
Next, the program illustrates how to change the amount of memory allocated for the array. It does this by using the %Realloc built-in function. You pass the %Realloc BIF the new size -- either smaller or larger -- and the pointer to the current block of memory (i.e., the pointer received back from the %Alloc BIF – or the %Realloc BIF if it was previously called). The %Realloc built-in function uses the pointer passed to it to identify the original block of memory.
If you are requesting more storage, the %Realloc BIF (as with the Realloc op-code and the CEECZST API) will always try to expand the current block of storage. If it cannot, then it will move (i.e., copy) the current block to a new location – one that is large enough for the new size. Therefore, the pointer the %Realloc built-in function returns may be different than the one you send it.
Note: You can use the %Realloc built-in function as many times as needed to adjust the size of the array according to the program's need.
The program then fills up elements 101-200. Again, you don't need to worry about the pointer, even if the %Realloc built-in function has moved the block of memory.
Finally, the program uses the new free-format version of the Dealloc op-code to deallocate – or free – the chunk of memory used by the array. You should always do this when you are done with the memory, as it frees it up for reuse. If you don't, the allocated memory will remain "locked-up" until the heap is discarded when the associated activation group is deleted (we'll explore this – and other important things to remember about heaps – in greater detail in the next installment).
Once again (and I can't stress this enough), notice that the program does not use or manipulate the pDynArr pointer variable anywhere. In fact, the only place pDynArr is specified is on the calls to the dynamic memory built-in functions and the Dealloc op-code. You do not need to use the pointer to access the array; the compiler does, but your code does not.
About the author: Ron Turull is editor of Inside Version 5. He has more than 20 years' experience programming for and managing AS/400-iSeries systems.
This was first published in August 2006