Basing Pointer Variables in RPG: The Basics
September 15, 2010 Ted Holt
When I began pursuit of my computer science degree eons ago, the only programming experience I had was business programming. Jumping from System/34 RPG II to Pascal was a shock, but learning Pascal was one of the best things that ever happened to me professionally. Learning Pascal introduced me to programming constructs that I had not seen before, including pointer variables, the topic of this article. If you’ve not mastered pointers, continue reading. Do it for your own edification.
RPG has two types of pointer variables: basing pointers, and procedure pointers. Both types of pointers hold memory addresses. Both types of pointers provide a way to indirectly access something else. This article deals only with basing pointers.
Your Address, Please
Basing pointers are variables, but they don’t store the types of data to which we are accustomed, such as names, addresses, account numbers, quantities, and currency amounts. Basing pointers store the memory addresses of data. They are indicated by an internal data type of asterisk (*) in position 40 of the definition specification. Here’s a basing pointer named Address.
DName+++++++++++ETDsFrom+++To/L+++IDc.Keywords+++++++++++++++++++++++++++++ D Address s *
A basing pointer has a null value by default. That is, it doesn’t point to any data at all.
So, if a basing pointer can store an address of data, but cannot store the data itself, where is the data to which the basing pointer points? The simple answer is that the data is somewhere else. That unknown “somewhere else” is represented by a based variable. Here is a basing pointer, PtrName, and a character variable, Name, to which the basing pointer points.
D PtrName s * D Name s 20a based(PtrName)
The Name variable has no place of its own in memory, so it must share memory that has been allotted to something else. To do that, assign the address of another variable to the pointer. (There is another method–use the %alloc function or the ALLOC op code to allocate memory for the based variable–but that will buy us nothing for this discussion.)
To see how it works, consider this example.
D AddressOfTheSelectedVar... D s * D TheSelectedVar... D s 20a based(AddressOfTheSelectedVar) D* D Name s 20a D Address s 20a D City s 20a D /free *inlr = *on; AddressOfTheSelectedVar = %addr(Name); TheSelectedVar = 'John Dilatory'; AddressOfTheSelectedVar = %addr(Address); TheSelectedVar = 'P. O. Box 1'; AddressOfTheSelectedVar = %addr(City); TheSelectedVar = 'Lost Angeles'; return;
In addition to the basing pointer and based variable, there are three static variables: Name, Address, and City. This short routine assigns values to the three static variables, but it does so indirectly. The second assignment statement retrieves the memory address of Name and stores that memory address in the basing pointer AddressOfTheSelectedVar. Now Name and TheSelectedVar refer to the same 20 bytes of memory, which means that changing one changes the other.
After this code runs, Name, Address, and City contain the values John Dilatory, P. O. Box 1, and Lost Angeles, respectively.
Let’s Be Practical
Let’s consider a more practical example. Suppose we have a command named DOIT (do it).
CMD PROMPT('Do it') PARM KWD(WHEN) TYPE(*TIME) MIN(1) MAX(6) + PROMPT('Time to do it')
Notice that DOIT has a list parameter, WHEN, which allows the user to specify from one to six times that “it” should be done.
DOIT WHEN(080000 123000 144500 180000)
The system formats the time values into a variable-length string. The first two bytes contain a binary number that tells how many values were keyed into the parameter. Following that are a series of one to six 6-byte character values. The system will send either eight, 14, 20, 26, 32, or 38 characters to the command-processing program.
While we’re supposing, let’s suppose that the command-processing program is written in RPG. How is the RPG program to interpret the list of time values? Before pointers, an RPG program would have to define the parameter with a length of at least 38 bytes.
Suppose the command is changed so that a maximum of 10 values can be keyed into the WHEN parameter. The length of the list parameter in the RPG program would have to be changed from at least 38 to 62.
However, if you use pointers to process the list, the program does not have to be changed. Look at this:
**** *entry plist D D DoItCPP pr D parmWhen 1a D DoItCPP pi D parmWhen 1a D ListPointer s * D NbrOfListElems s 5i 0 based(ListPointer) D TimeInList s 6a based(ListPointer) D NbrOfTimes s 5i 0 D Ndx s 10i 0 D TimeVar s 6a /free *inlr = *on; // How many time values were passed in? ListPointer = %addr(parmWhen); NbrOfTimes = NbrOfListElems; // Advance to the first time value. ListPointer += 2; // Loop to extract the time values. for Ndx = 1 to NbrOfTimes; TimeVar = TimeInList; // Here's the place do something with TimeVar. // Advance to the next time value in the list. ListPointer += %size(TimeInList); endfor; return;
The input parameter, parmWhen, is defined as a 1-byte character value. That’s obviously incorrect, but it doesn’t matter, because all we need is the address of the parameter.
In the calcs, extract the number of time values from the first 2 bytes of the parameter.
D ListPointer s * D NbrOfListElems s 5i 0 based(ListPointer) D NbrOfTimes s 5i 0 /free ListPointer = %addr(parmWhen); NbrOfTimes = NbrOfListElems;
Advance the pointer 2 bytes so that it points to the first 6-byte time value.
ListPointer += 2;
Process the time values in the list.
// Loop to extract the time values. for Ndx = 1 to NbrOfTimes; TimeVar = TimeInList; // Here's the place do something with TimeVar. // Advance to the next time value in the list. ListPointer += %size(TimeInList); endfor;
Each iteration loads the next time into TimeVar and advances the pointer 6 bytes so that it points to the next time value in the list. After the last iteration, ListPointer is pointing to the first byte in memory that follows the list, but that’s OK as long as we don’t use ListPointer while it has that value.
If someone changes the MAX parameter of the DOIT command, the system will send strings of other lengths, but since the length of parmWhen is not specified in the parameter definition, the RPG program will not need to be changed.
Broaden Your Horizons
If you don’t know how to use basing pointers, you may as well learn. Basing pointers are not difficult to understand. They especially come in handy when dealing with the data returned from certain APIs. And they’re fun.