Pointer variables, based variables, overlaid variables—do you know all the variable types and the right ways to use them?
Editor's Note: This article is an excerpt from the MC Press book Complete CL.
Variables are program data that are allowed to change values. They have names and are stored in memory for the duration of the program. Variables are of different types: character, decimal, signed integer, unsigned integer, logical, and pointer.
Declaring Variables
Before you can use a variable in a CL program, you must first define the variable or declare it to the program. Declaring variables must be done at the very beginning of the program, after the PGM statement.
A variable name can contain up to 11 characters. The first character must be an ampersand (&), which identifies the rest as a variable name. The second character must be a letter (A to Z) or one of the characters @, #, or $. All characters following the second can be letters, digits (0 to 9), or the characters @, #, $, or _ ("break"). You can use the break character to improve readability. &A, &OUTQ, and &VOL_ID are examples of valid variable names.
Examples of invalid variable names would include &NBRDLTPGMMSG, which is too long; &INV%TOTAL, which contains an invalid character; and &4TOTAL, because a digit may not follow the ampersand.
Variables are declared to the program with the Declare (dcl) command. Each variable must be declared with a separate dcl command. The dcl command has eight parameters:
- Var: Receives the name of the variable being declared. Var is a required parameter.
- Type: Receives the values *char, *dec, *INT, *UINT, *lgl, or *PTR, depending on whether the variable type is character, decimal, signed integer, unsigned integer, logical, or pointer. This parameter is required, too.
- STG: Indicates the storage method; that is, the way the variable is allocated in memory. It's optional.
- Len: Indicates the length of the variable. For character, integer, and logical variables, this value indicates the number of bytes to be allocated to the variable. For decimal variables, this value indicates the number of digits the variable should hold. This parameter is optional.
- Value: Indicates the initial value for the variable. It's optional too.
- BASPTR: Indicates the pointer upon which the variable is based. It's optional.
- DEFVAR: Indicates the variable with which this variable shares memory. This parameter is optional.
- ADDRESS: Indicates the initial value of a pointer. It is optional.
Note that, for decimal variables, len can contain two numbers: the total length of the variable (how many digits in length), and the number of decimal places. len(5 2), for example, could hold a number as large as 999.99.
Because logical variables are always 1 byte long, len(1) is optional. When the len parameter is omitted, the compiler assumes len(32) for character variables, len(15 5) for decimal variables, LEN(4) for integer variables, or len(1) for logical variables.
When you omit the len parameter but supply an initial value with the value parameter, the value parameter dictates the length of the variable that will be assumed by the compiler. For example, consider the two cases shown in Figure 1.
DCL VAR(&NAME) TYPE(*CHAR) VALUE('QSYSOPR')
DCL VAR(&PI) TYPE(*DEC) VALUE(3.14159265)
Figure 1: Examples of declaring the length of the variable based on length of data in the value parameter.
When you compile the program that includes the variables shown in Figure 1, &name will have a length of seven (the initial value, qsysopr, contains seven characters). &pi will have a length of (9 8) because its initial value has nine digits (of which eight fall to the right of the decimal point).
Character variables can be as long as len(32767). Decimal variables are len(15 9). Logical variables are always len(1). Table 1 summarizes the rules for lengths for various data types.
Table 1: Maximum and Default Lengths for Variables |
||
Variable Type |
Maximum Length |
Default Length |
TYPE(*CHAR) |
LEN(32767) |
LEN(32) |
TYPE(*DEC) |
LEN(15 9) |
LEN(15 5) |
TYPE(*INT) |
LEN(4) |
LEN(4) |
TYPE(*UINT) |
LEN(4) |
LEN(4) |
TYPE(*LGL) |
LEN(1) |
LEN(1) |
TYPE(*PTR) |
n/a |
n/a |
Note: The only acceptable values for length of both *INT and *UINT are 2 and 4. |
Although the LEN parameter can be omitted when the default value is suitable, it does make the program harder to read if the programmer does not know the default length. To make your programs easier to read, you should always use the LEN parameter.
Where Variables Can Be Used
When you execute commands from the keyboard, you always enter constant values for all parameters. Consider the Display Library (dsplib) command shown in Figure 2.
DSPLIB LIB(QGPL) OUTPUT(*PRINT)
Figure 2: The DSPLIB command with constant values.
As shown in Figure 2, parameter lib is receiving a four-character constant value (qgpl), while parameter output is receiving a six-character constant (*print).
When you code a dsplib command in a CL procedure, however, you have the option of using constants or variables in the parameters. Variables add flexibility when such flexibility is needed. If variable &a has a value of qgpl and variable &b has a value of *print, the CL program statement shown in Figure 3 produces the same result as the statement presented in Figure 2.
DSPLIB LIB(&A) OUTPUT(&B)
Figure 3: The DSPLIB command with variables as parameters.
Don't be fooled into believing that variables can do all sorts of magic. If variable &c has the value lib(qgpl) and variable &d has the value OUTPUT(*print), the statement shown in Figure 4 will not produce the same result.
DSPLIB &C &D
Figure 4: Invalid use of variables in a DSPLIB command.
Because it has no way of knowing what values &c and &d will have, the text editors still accept this statement as valid. The source member even compiles correctly because the compiler assumes you are entering the parameters positionally. When the procedure executes, however, it comes to a screeching halt with an error message. The statement is interpreted as shown in Figure 5.
DSPLIB LIB('LIB(QGPL)') OUTPUT('OUTPUT(*PRINT)')
Figure 5: Results of using the code as shown in Figure 4.
Now assume that variable &e has the value dsplib lib(qgpl) output(*print). If you code Figure 6 as a statement, the editor's syntax checker will reject the statement.
&E
Figure 6: Invalid use of a variable to code a CL command.
For the example shown in Figure 6, &E isn't a command; it's a variable. Only commands can be coded in CL programs. Executing commands dynamically requires the use of program qcmdexc.
Another example points to an additional source of errors. Suppose you execute from the keyboard the command shown in Figure 7.
CHGJOB OUTQ(QGPL/QPRINT) RUNPTY(20) LOG(4 0 *NOLIST)
Figure 7: The CHGJOB command with constant values as parameters.
If you code this command in a CL program, all parameters can have variables, as shown in Figure 8.
CHGJOB OUTQ(&A/&B) RUNPTY(&C) LOG(&D &E &F)
Figure 8: The CHGJOB command with variables as parameters.
A qualified name such as qgpl/qprint cannot be replaced by only one variable. Two variables, as in &a/&b, take the place of the qualified name. Similarly, a list must be coded with one variable per element of the list, but never with one variable for the entire list. For example, you cannot use the statement shown in Figure 9 to produce the same results as the example shown in Figure 8.
CHGJOB OUTQ(&G) RUNPTY(&H) LOG(&I)
Figure 9: Invalid use of variables in parameters.
The statement is incorrect if &g has the value qgpl/qprint and &i has the value 4 0 *nolist. A single variable can contain only a single value. Outq(&g) would be acceptable if &g had the value qprint only because the command would then use the library list to locate output queue qprint.
Overlaid Variables
Overlaying variables means defining two variables so that they occupy the same memory location. Since the two variables share the same memory, changing one changes the other.
To make one variable overlay another, use the STG (storage) and DEFVAR (defined on variable) parameters of the declare command. The STG parameter takes the value *DEFINED. In the DEFVAR parameter, indicate the name of another variable and the first position of that variable that is to share memory. Look at the example in Figure 10.
DCL &QualObj *CHAR LEN(20)
DCL &Object *CHAR LEN(10) STG(*DEFINED) DEFVAR(&QualObj 1)
DCL &Library *CHAR LEN(10) STG(*DEFINED) DEFVAR(&QualObj 11)
Figure 10: Examples of overlaid variables.
There is no difference between the contents of the &Object variable and the first ten positions of &QualObj, because &Object overlays &QualObj at the first position and overlays ten positions. In the same way, &Library is an alternate name for the last ten positions of &QualObj.
Pointer Variables
A pointer variable contains the address of another variable. To define a pointer variable, specify TYPE(*PTR) in the DCL command and do not code the LEN parameter. In Figure 11, the &NextObj variable is a pointer.
DCL VAR(&NextObj) TYPE(*PTR)
Figure 11: A pointer variable holds the address of another variable.
Since a pointer variable does not hold data, it is of little value until it is assigned the address of another variable. To assign an address to a pointer in a variable declaration, use the ADDRESS parameter of the DCL command. To assign an address in calculations, use the %ADDRESS function. You may shorten %ADDRESS to %ADDR. Look at the two pointer variables in Figure 12.
DCL VAR(&TopOfList) TYPE(*PTR) ADDRESS(&List)
DCL VAR(&NextObj) TYPE(*PTR)
DCL VAR(&Pattern) TYPE(*CHAR) LEN(10)
DCL VAR(&List) TYPE(*CHAR) LEN(252)
Figure 12: Example of two pointer variables.
The &TopOfList variable contains the memory address of the &List variable. The CHGVAR command assigns the address of &Pattern to the &NextObj pointer variable.
To modify the value of a pointer (i.e., to do "pointer arithmetic"), use the %OFFSET (or %OFS) function. In Figure 13, the &NextObj pointer is adjusted to point to the memory address ten bytes after its current position.
CHGVAR VAR(%Offset(&NextObj)) VALUE(%Offset(&NextObj)+10)
Figure 13: Use the %OFFSET function for pointer arithmetic.
Based Variables
The compiler does not allocate storage to based variables. Instead, the based variable uses a pointer in order to access the storage that has been assigned to another variable. In Figure 14, the &File variable represents two other variables, &SalesHist and &CurrSales, by means of pointer &pFile.
DCL VAR(&pFile) TYPE(*PTR)
DCL VAR(&File) TYPE(*CHAR) LEN(20) STG(*BASED) BASPTR(&pFile)
DCL VAR(&SalesHist) TYPE(*CHAR) LEN(10)
DCL VAR(&CurrSales) TYPE(*CHAR) LEN(252)
CHGVAR VAR(&pFile) VALUE(%ADDR(&SalesHist)
/* ... do something using &File ... */
/* ... whatever you do will happen to &SalesHist ... */
CHGVAR VAR(&pFile) VALUE(%ADDR(&CurrSales)
/* ... do something using &File ... */
/* ... whatever you do will happen to &CurrSales ... */
Figure 14: A based variable indirectly accesses other variables.
LATEST COMMENTS
MC Press Online