|
|||||||
|
|
![]() |
|
|
Back to Basics: Pointers in RPG by Kevin Vandever You might think that pointers are new to the RPG world, but, in fact, that is not the case. Pointers have been around for a long time, but it is only recently that we as RPG programmers could really take advantage of them beyond what OS/400 provided us. In this article, I am going to discuss the basics of pointers and show you some ways to use them. Right in Front of Our Eyes All This Time Pointers already exist in your day-to-day RPG world and you probably don't even know it. When you pass parameters on an OPM program call, or by reference in an ILE bound call, you are using pointers. When you call a program that passes parameters, you don't pass the actual values of those parameters; rather, you pass the address, in memory, of the parameter fields. Then the called program uses those addresses to access the values of the fields. Now, we can't manipulate those pointers or really do anything else about them; we just use them. But they are pointers, and by understanding the very basics we can begin to use pointers that we can manipulate. Those are the pointer data types I want to talk about. What Are Pointer Data Types? A pointer data type is a reference to a variable, an array, or a data structure. It is a field that contains a memory address. Let's take a field defined in a D-spec: D MyName s 30 Now we can assign a value to that variable, as follows: MyName = 'Kevin Vandever'; Or, for you traditionalists (or those not yet on V5R1): C Eval MyName = 'Kevin Vandever' In either case, what I've done is assigned a value to the space taken up by the variable, MyName. The memory for MyName is allocated when the program starts up, or loads into memory. The value of Kevin Vandever occupies the space of MyName. A pointer comes in when we control the actual address in memory, where MyName exists, and not the variable itself. Check out this code: D MyName S 30A Based(Pointer1) D Pointer1 S * Inz I have the same MyName variable, but now I've based it on a pointer by using the keyword Based. Then I defined my pointer, Pointer1. I can manipulate the memory as well as the variable. For example, I could set Pointer1 to a different memory address and thus change the contents of MyName. I could then assign a new value to MyName and thus change the contents that the address in Pointer1 points to. Get it? If not, don't worry; we'll take a closer look at basing pointer data types as well as another pointer data type. Basing Pointers The above example is an illustration of a basing pointer data type. We now have the ability to define data whose place in memory is designated by a pointer. This way, the variable does not have a fixed place in memory but is designated by the way that you set the pointer that the variable is based on. A field, array, or data structure is based on a variable by using the Based keyword and assigning a pointer that will be used as the designator of memory. Then you define the pointer as a stand-alone field and place an asterisk (*) in the internal data type column of the D-spec. A pointer is 16 bytes long and may not have to be defined. If, in my example above, I hadn't coded the second D-spec defining Pointer1, the compiler would have done so for me using the name provided in the Based keyword. You must explicitly define a pointer if it is not used as a based pointer. Now that you know how to define a basing pointer, it's time to manipulate that data a little bit. Here is a snippet of code that defines some basing pointers and messes around with them:
D NewName S 40A
D MyName S 30A Inz('Kevin Vandever')
D HerName S 30A Based(Pointer2)
D Pointer1 S * Inz(%Addr(MyName))
D Pointer3 S * Inz
* After the D specs, Pointer1 points to Kevin Vandever
* Now, assign Pointer2 a memory address and HerName a value
C Eval Pointer2 = %Addr(NewName)
C Eval HerName = 'Corina Vandever'
* Allocate storage for Pointer3
C Eval Pointer3 = %Alloc(40)
C Eval Pointer3 = %Addr(NewName)
* Another way to initialize storage
C Eval %Str(Pointer3 : 40) =
C %Str(Pointer2 : 6) +
C ' And ' + %Str(Pointer1)
C NewName Dsply
C Eval *InLr = *On
What did I do in that code? Not much, really. I just showed some of the different ways to manipulate storage. First, I defined three fields: NewName, MyName, and HerName. NewName and MyName are normal alpha fields, defined when the program loads. HerName is also an alpha field, but it is based on Pointer2. I then defined three pointers: Pointer1, Pointer2, and Pointer3. Pointer2 was implicitly defined because it was used in the Based keyword. I didn't have to define it in my D-specs. Pointer2 and Pointer3 have been initialized to nulls (*NULL), which is the default if nothing is entered in the INZ keyword. Pointer1, however, has been initialized a little differently. I used the address built-in function (%Addr) to assign the storage address of the field MyName to Pointer1. In my C-specs, I first assign a memory address to Pointer2 because it is Null right now. I used the %Addr built-in function again and assigned Pointer2 the storage address of NewName, which was defined in the D-specs. I couldn't use HerName here, because that field has not been assigned storage yet. Remember, based variables are defined dynamically, not when the program loads. I then define a value to HerName, which I can now do because its based pointer, Pointer2, has been assigned a storage address. Because HerName is based on Pointer2, and Pointer2 contains the address of NewName, after I assign Corina Vandever to HerName, the value of NewName is also Corina Vandever, because it shares the same storage as HerName. Take a minute to let that soak in. Next, I use the %Alloc built-in function to allocate storage for Pointer3. In this case, I allocate 40 bytes of storage. This just allocates storage; it does not base the pointer on any variable. If I wanted to do that, I could use the %Addr built-in function to not only allocate 40 bytes of storage but also assign it the storage occupied by a specific variable--in this case, the NewName variable. There is no need for both lines of code. I just wanted to show you the different ways to allocate storage. The reason why I would rather use the %Addr function in this case is that I want to manipulate Pointer3 and see that manipulation reflected in the NewName variable. Let's look at that code now. I use the Get or Store Null Terminated String built-in function, %Str, to build the null terminated string Corina and Kevin Vandever. This built-in function only works with based pointers and allows you to build or extract null-terminated strings from storage. Finally, I display the NewName variable for all to see. So why would you want to do any of this? First of all, dynamic storage allocation potentially allows you to manage your storage better and more efficiently. This might come in handy when working with large or inconsistently sized data structures that might otherwise allocate unnecessary storage. I have used them to dynamically allocate storage based on data received from a data queue. I receive anywhere from 50 to 64,000 bytes of data per request. I didn't want to statically define a 64K data structure to hold the data, especially since the majority of the entries were on the small side. So I used pointers to allocate storage as I needed it. That way, my structure was only as large as the number of byes received from the queue. Another reason to learn about pointers is that many C-based APIs accept pointers as parameters and in order to work with those APIs you must be able to properly define and use pointers in your program. Now let's take a look at another type of pointer data type. Procedure Pointers This type of pointer allows you to point to procedures or functions that are bound to your program. This makes it very easy to dynamically reference the procedure that you want to call. Procedure pointers are defined much the same way as the based pointers, except that when you define a procedure pointer, you do so by using the procedure pointer (ProcPtr) keyword to tell the compiler that this is a procedure pointer. Let's take a look at a code snippet:
D Pointer1 S * ProcPtr
D MyProc PR 4 ExtProc(Pointer1)
D Parm1 20 Value
C Eval Pointer1 = %PAddr('SomeProc')
C Eval ReturnCode = MyProc(Parm1)
I first define the pointer, Pointer1, and make it a procedure pointer by using the keyword ProcPtr. Then I define the prototype for my procedure as I normally would, except that instead of hard-coded procedure or function as the parameter to the external procedure (ExtProc) keyword, I refer to the pointer that I just defined. I then use the pointer address built-in function (%PAddr) to assign the address of a procedure to my pointer. This %PAddr function works just like the %Addr function I used with regular pointers, except that it is used with procedure pointers. Next, I call my procedure like I would any other procedure. MyProc is really a call to SomeProc, because that is the procedure my pointer points to. If I wanted to call another external procedure, I could easily do so by assigning the address of another procedure to Pointer1, then calling MyProc again--assuming that the procedure shared the characteristics of the original prototype. The procedure pointer data type allows you to define generic procedure prototypes and to dynamically plug in the external procedure you really want to call. This technique could add flexibility to your applications and cut down on development time. Point Away I have covered the very basics of pointer data types and provided you with a few reasons why you might want to use them. Give them a try and come up with some of your own ideas--not that you can't use mine, but I may have sold pointers short. If you do use them, you have to be very careful. With the power of being able to manipulate memory comes the potential for data corruption that just wouldn't happen if you didn't use pointers. But with that power also comes the potential for increased flexibility and efficiency, and you have to decide if the risk is worth the potential reward.
|
Editors
Contact the Editors |
| Copyright © 1996-2008 Guild Companies, Inc. All Rights Reserved. |