previous next top contents index framed top this page unframed
ARRAYs are allocated and accessed in two different ways, each using its own syntax:
Dynamic and inplace ARRAYs are similar in many ways; they differ mainly in the ways in which they are created and destroyed. In MAINSAIL documentation, the term ARRAY can mean either an inplace ARRAY or a dynamic ARRAY, unless the context makes it clear that only one kind of ARRAY is intended.
A field of an ARRAY is accessed by means of a subscripted variable (as described in Section 12.6), which is an ARRAY variable (inplace or dynamic) followed by one or more (LONG) INTEGER indices in square brackets.
12.1. Inplace ARRAYs
An inplace ARRAY is allocated automatically as a local variable or
as a component of some other object;
this is the same way in which values of simple data types, such as
INTEGER, are allocated.
It is not possible to call the system PROCEDURE new to allocate
an inplace ARRAY.
An inplace ARRAY is accessed through an inplace ARRAY variable. An inplace ARRAY variable is declared with the keyword $INPLACEARRAY, preceded by the ARRAY elements' type, and followed by bounds information in parentheses, as in:
STRING $INPLACEARRAY(1 TO 10) myArray;
Assignment of one inplace ARRAY to another copies each element of the ARRAY from the source to the destination.
Two simple ARRAY variables (that are not $REFERENCE parameters; see Section 7.5.4) cannot be aliases for each other; each has its own copy of each element of the ARRAY, and a change to an element of one of the ARRAYs is not reflected in an element of another simple inplace ARRAY variable. This is also true of simple variables of types like INTEGER; a change to a simple INTEGER variable i does not change the value of another simple INTEGER variable j. Inplace ARRAY variables are said to have “value semantics” because their values are independent of one another; this contrasts with the “pointer semantics” of dynamic ARRAY variables.
An inplace ARRAY is never disposed explicitly, and never garbage
collected;
it goes away automatically when the object of which it is a component
is reclaimed
or when the PROCEDURE in which it is a local variable returns.
12.2. Dynamic ARRAYs
A dynamic ARRAY is never allocated automatically; it is allocated
by a call to a the system PROCEDURE new.
This system PROCEDURE takes a dynamic ARRAY variable,
and possibly
(LONG) INTEGER ARRAY bounds, and allocates a dynamic
ARRAY object
to which the dynamic ARRAY variable is made to point.
A dynamic ARRAY object is accessed through a dynamic ARRAY variable, which is essentially a POINTER to the contents of the ARRAY (in fact, an unclassified POINTER variable can also point at an ARRAY's contents). Information about the bounds and element type of ARRAY objects to which the ARRAY variable is allowed to point are usually included in the ARRAY variable declaration, as described in Section 12.3.
Assignment of one dynamic ARRAY variable to another makes the destination ARRAY point to the same object as the source ARRAY.
Two dynamic ARRAY variables may be aliases for the same dynamic ARRAY object; that is, they may both point to the same ARRAY object, and a change to any of the ARRAY's elements using one variable is reflected when the same element is accessed through the other variable. Dynamic ARRAY variables are said to have “pointer semantics”, in contrast to the “value semantics” of inplace ARRAY variables.
A dynamic ARRAY may be reclaimed by passing a POINTER to it to the system PROCEDURE dispose, or it may be reclaimed automatically by the garbage collector if it becomes inaccessible.
NULLARRAY specifies the Zero dynamic ARRAY
(that is, a dynamic ARRAY that does
not point at an ARRAY object).
It is typeless and dimensionless.
12.3. ARRAY Declarations
The form of an ARRAY declaration is:
type arrayKeyword (l1 TO u1, ..., lm TO um) v1, ..., vn
where arrayKeyword is $INPLACEARRAY to declare an inplace ARRAY or ARRAY to declare a dynamic ARRAY, type is the data type of the ARRAYs, li and ui specify the lower and upper bounds, respectively, of the ith dimension, and the vi are the identifiers of the ARRAYs being declared.
Each li or ui is either a (LONG) INTEGER constant expression, with li less than or equal to ui, or an asterisk (*). INTEGER and (LONG) INTEGER constants li and ui may be freely mixed; there is no semantic distinction between bounds declared as INTEGERs and those declared as LONG INTEGERs. The asterisk indicates that the bound is not known at the point of declaration. A single * may be used in place of * TO *.
The data type and/or the parenthesized bounds list may be omitted from a dynamic ARRAY declaration, in which case the ARRAY cannot be used for element access, although it can still be used for pseudofield access (see Section 12.8). Such a typeless or dimensionless ARRAY may be passed as a parameter (see Section 7.9) or be assigned to or compared with some other ARRAY (see Sections 12.10.1 and 12.10.3).
For an inplace ARRAY, the data type must be specified, and all bounds must be specified, except that the upper bound of the first dimension of an unsized ARRAY may be declared as * in some circumstances, as described in Section 12.14.
The data type of the ARRAY must be one of the standard MAINSAIL data types (BOOLEAN, (LONG) INTEGER, (LONG) REAL, (LONG) BITS, STRING, POINTER, inplace record, $PROCVAR, ADDRESS, or CHARADR); it cannot be an ARRAY. That is, it is not possible to have ARRAYs of ARRAYs.
MAINSAIL supports ARRAYs of up to three dimensions. The number of dimensions is the number of bound pairs specified in parentheses after the word ARRAY in the ARRAY declaration.
An example of an inplace ARRAY ia with 11 INTEGER elements is:
INTEGER $INPLACEARRAY(0 TO 10) ia;
An example of a 3-dimensional dynamic ARRAY da with 1 for the lower bound of each dimension and the upper bound unspecified is:
STRING ARRAY(1 TO *,1 TO *,1 TO *) da;
12.3.1. Inplace ARRAY Bounds
In an inplace ARRAY declaration,
the bounds must be declared as (LONG) INTEGER constants, except
that
the upper bound of the first dimension of an unsized ARRAY may be
declared as *,
as described in Section 12.14.
12.3.2. LONG ARRAYs
An obsolescent feature of MAINSAIL
allows the keyword LONG to appear in an
ARRAY declaration before
the arrayKeyword ARRAY;
such an ARRAY is said to be a LONG ARRAY, and any
ARRAY declared without LONG is a short ARRAY.
However, there is no longer any implementation difference between
short and LONG ARRAYs, except in the
GENERIC PROCEDURE instance
selection algorithm, and there only for compatibility with previous
versions of MAINSAIL;
see Section 7.16.3.
You should not declare a LONG ARRAY in new MAINSAIL code.
The keyword LONG is not, and never has been, permitted before
$INPLACEARRAY.
12.4. Inplace ARRAY Allocation
Inplace ARRAY allocation occurs implicitly.
If the inplace ARRAY is a PROCEDURE local variable
(a PROCEDURE parameter is considered a local variable),
it is allocated automatically on entry to the PROCEDURE.
Otherwise, the inplace ARRAY must be a component of some other
object; it is allocated when the enclosing object is allocated.
The contents of an inplace ARRAY are cleared (all elements are set to Zero) when an inplace ARRAY is allocated, even when the inplace ARRAY is a local variable.
As an example, consider the following code:
CLASS c (INTEGER i; STRING ARRAY(*) sAry);
$RECORD(c) $INPLACEARRAY(1 TO 100) ary;
PROCEDURE proc (INTEGER $INPLACEARRAY(1 TO 3) ary1);
BEGIN
INTEGER $INPLACEARRAY(1 TO 3) ary2;
new(ary[1].sAry,-10,10);
END;
ary1 and ary2 are allocated and cleared on entry to proc; i.e., at the start of proc, all of ary1's and ary2's elements are 0. ary, being an outer variable, was allocated and cleared when the current data section was allocated.
ary is an inplace ARRAY of inplace records,
each of CLASS c.
ary[1] is therefore an inplace record of CLASS c;
ary[1].sAry is a dynamic ARRAY field of that inplace record.
new(ary[1].sAry,-10,10) allocates that dynamic
ARRAY field with
lower bound -10 and upper bound 10.
12.5. Dynamic ARRAY Allocation
It is the programmer's responsibility to allocate a dynamic
ARRAY before an
attempt is made to access its elements. This is accomplished with the
system PROCEDURE new.
The general form of a call to new
for dynamic ARRAY allocation is:
new(v,l1,u1,...,ln,un)
where v is the ARRAY variable to be allocated, and li and ui are (LONG) INTEGER expressions for the lower and upper bounds of the ith dimension. new clears (sets to Zero) all the elements of the newly allocated ARRAY.
Any bound declared as a constant may be omitted from new (in which case all remaining arguments must also be omitted) as long as all subsequent bounds were also declared as constants; the compiler fills in the missing bounds from the information given in the declaration. The compiler issues an error message if some bound is declared as a constant, but the corresponding argument to new is specified as a non-constant expression or a constant with some other value. For example, if arr1 is declared as:
INTEGER ARRAY(1 TO *) arr1
and n is a (non-constant) INTEGER expression, then these calls to new are legal:
new(arr1,1,10)
new(arr1,1,n)
and these are illegal:
new(arr1,n,20)
new(arr1,2,10)
new(arr1)
But if arr1 were declared:
INTEGER ARRAY(1 TO 15) arr1
then:
new(arr1)
would be legal and equivalent to:
new(arr1,1,15)
and also to:
new(arr1,arr1.lb1,arr1.ub1)
(see Section 12.8).
A dynamic ARRAY variable may be allocated any number of times (though often it is allocated just once). Each call to new makes the ARRAY variable point to a different, newly allocated ARRAY object; elements are not automatically copied from the object to which the ARRAY variable previously pointed. A one-dimensional dynamic ARRAY's upper bound may be changed during program execution by use of the system PROCEDURE newUpperBound (see Section 42.10), which does copy the values to the reallocated ARRAY.
If an ARRAY is declared with constant bounds, then whenever it is not NULLARRAY, it must match its declaration. If an ARRAY declared with constant bounds is reallocated as or made to point to an ARRAY with bounds different from those declared as constants, the effect is undefined. For example, although the compiler permits the following:
INTEGER ARRAY(1 TO 10) ary1;
INTEGER ARRAY(*) ary2;
...
new(ary2,0,20);
...
ary1 := ary2;
the use of ary1 after the assignment statement has undefined effects, since its bounds are 0 to 20, but it is declared to have the bounds 1 to 10.
Likewise, if an ARRAY is declared with bounds, then the number
of dimensions in the ARRAY must be as declared, and if the
ARRAY is typed, its type must be as declared.
The effect is undefined if an ARRAY variable is declared one way
but then made to point to an ARRAY with different characteristics.
12.6. Accessing an ARRAY Element
An (inplace or dynamic)
ARRAY element is accessed with a subscripted variable, i.e.,
an ARRAY variable or parenthesized expression
followed by a bracketed list of (LONG) INTEGER expressions.
There is one subscript for each dimension of the ARRAY, and the
subscripts are separated by commas, e.g., a[e1],
a[e1,e2], a[e1,e2,e3].
A subscript may be an INTEGER or a LONG INTEGER; its data type does not have to match the data type of the corresponding bound in the ARRAY variable's declaration.
To refer to the eighth element of a one-dimensional ARRAY a with a lower bound of 1, the subscripted variable a[8] or a[8L] may be used. If a two-dimensional ARRAY b with both lower bounds equal to one is viewed as a matrix, b[8,3] (or b[8L,3L], etc.) is the element in the eighth row and third column.
Each subscript must be within the bounds declared for its dimension (see Section 12.3). If checking is in effect (see Section 16.3), an error message is issued at runtime if, for any subscript, this is not the case (except that the first subscript of an unsized inplace ARRAY (see Section 12.14) cannot in general be checked against the upper bound of the ARRAY's first dimension). If checking is not in effect, the effect is undefined if a subscript is out of bounds.
A dynamic ARRAY must be allocated before an
attempt is made to access its elements (or pseudofields; see
Section 12.8).
If checking is in effect,
an error message is issued at runtime if
the ARRAY part of a subscripted variable has not been allocated.
If checking is not in effect, the use of an unallocated or
uninitialized ARRAY variable is undefined.
12.7. ARRAY Bounds and Subscript Calculation Overflow
The ARRAY bounds rule states that an ARRAY's subscript calculation must never overflow the range $minLongInteger to $maxLongInteger. Specifically, the results of the following subscript calculations must, for every combination of valid subscripts, be within the range $minLongInteger to $maxLongInteger at every step:
| a[i]: | i * elemSize |
| a[i,j]: | ((i * e2) + j) * elemSize |
| a[i,j,k]: | (((i * e2) + j) * e3 + k) * elemSize |
where elemSize is the number of storage units occupied by each ARRAY element.
The ARRAY bounds rule is not enforced;
violations produce undefined results.
12.8. ARRAY Pseudofields
For both inplace and dynamic ARRAYs, information about an ARRAY
is available through ARRAY pseudofields.
Syntactically, the pseudofields of an ARRAY a are accessed by means of a pseudofield variable of the form a.pseudofieldName. The pseudofields available are shown in Table 12–1.
The values of the INTEGER bounds pseudofields (lb1, ub1, etc.) are undefined if the corresponding LONG INTEGER pseudofields ($lb1, $ub1, etc.) cannot be represented as an INTEGER.
Reference to the pseudofields representing second- and third-dimensional bounds generates a compiletime error message for a one-dimensional ARRAY, and reference to the pseudofields representing third-dimensional bounds generates an error for a two-dimensional ARRAY. Reference to any bound pseudofield generates an error for a dimensionless ARRAY. Reference to the ub1 or $ub1 pseudofield generates a compiletime error for an unsized inplace ARRAY (see Section 12.14).
The pseudofields $arrayType and $dimension can be accessed using any ARRAY variable, even one declared without type or bounds.
Table 12–1. ARRAY Pseudofields
| Field | Description |
|---|---|
| name | STRING name of the ARRAY |
| lb1 | INTEGER lower bound of 1st dimension |
| ub1 | INTEGER upper bound of 1st dimension |
| lb2 | INTEGER lower bound of 2nd dimension (if any) |
| ub2 | INTEGER upper bound of 2nd dimension (if any) |
| lb3 | INTEGER lower bound of 3rd dimension (if any) |
| ub3 | INTEGER upper bound of 3rd dimension (if any) |
| $lb1 | LONG INTEGER lower bound of 1st dimension |
| $ub1 | LONG INTEGER upper bound of 1st dimension |
| $lb2 | LONG INTEGER lower bound of 2nd dimension (if any) |
| $ub2 | LONG INTEGER upper bound of 2nd dimension (if any) |
| $lb3 | LONG INTEGER lower bound of 3rd dimension (if any) |
| $ub3 | LONG INTEGER upper bound of 3rd dimension (if any) |
| $arrayType | type code of ARRAY's data type (an extended type code if the data type is explicitly sized; see Section 3.12 and Appendix A) |
| $dimension | number of dimensions of ARRAY |
If any bound of an ARRAY is declared constant, the corresponding bound pseudofield is evaluated at compiletime (see Section 12.8.1). For example, if an ARRAY ary is declared as:
INTEGER ARRAY(1 TO 10) ary;
then ary.lb1 is treated by the compiler as the constant 1 and ary.ub1 as the constant 10. Thus, it is possible to say (if allocating ary in an area myArea):
new(ary,ary.lb1,ary.ub1,myArea);
ary.$arrayType and ary.$dimension are similarly evaluated at compiletime if the type and number of dimensions, respectively, are specified in ary's ARRAY declaration. They are evaluated at runtime for a typeless or dimensionless ARRAY.
ary.name is evaluated at compiletime for an inplace ARRAY and at runtime for a dynamic ARRAY.
12.8.1. NULLARRAY Checking for Compiletime-Evaluated
Pseudofields of ARRAYs
Since some of the pseudofields (e.g., bounds declared constant)
of an ARRAY ary are evaluated at compiletime,
no check is made whether ary is NULLARRAY when using
ary to access
these pseudofields, even when checking is
in effect; you can use the compiletime-evaluated
pseudofields even when the ARRAY is not allocated.
This behavior will not change in future releases when the ARRAY
pseudofields are accessed directly through the ARRAY identifier,
e.g., ary.lb1, where ary is a simple variable.
Currently, ARRAY bounds pseudofields may also be computed at compiletime when the ARRAY is a record field accessed through a POINTER; if declared constant, the bounds pseudofields are computed at compiletime, and no use of the POINTER occurs at runtime, so no NULLPOINTER checking is performed. This behavior, however, is subject to change.
The following program:
BEGIN "aryPtr"
CLASS c (
INTEGER ARRAY(1 TO 3) dynAry;
STRING $INPLACEARRAY(2 TO 4) inplAry;
);
INITIAL PROCEDURE;
BEGIN
POINTER(c) p;
p := NULLPOINTER;
write(logFile,p.dynAry.lb1,eol,p.dynAry.ub1,eol,
p.inplAry.lb1,eol,p.inplAry.ub1,eol);
END;
END "aryPtr"
in this release of MAINSAIL, writes:
1
3
2
4
and does not issue a NULLPOINTER access error for p,
because all the bounds displayed were declared as constants.
12.9. Inplace ARRAY Operations
Operations on inplace ARRAYs include element access, assignment,
and parameter passing.
Element access is described in Section 12.6.
12.9.1. Assignment
Inplace ARRAY assignment copies ARRAY elements from one
ARRAY to another.
Inplace ARRAYs can be assigned (with the assignment operator, e.g. ary1 := ary2) if they are both sized (see Section 12.14) and if ary1 and ary2 are assignment compatible, which means both of the following must be true:
For example, if the element types are records, they must be of the same CLASS. If the element types are POINTERs or ADDRESSes and both types are classified, then the types' CLASSes must be related and, if the STRICTCLASSES compiler directive is in effect, the CLASS of ary2's element type cannot be a prefix CLASS of the CLASS of ary1's element type. The rules for allowable assignments involving $PROCVARs are too detailed to summarize here; see Section 8.3.
The assignment ary1 := ary2 of inplace ARRAYs ary1 and ary2 copies all of ary2's elements to ary1. The assignment can be part of an assignment expression:
INTEGER $INPLACEARRAY(1 TO 10) ary1,ary2,ary3;
. . .
ary3 := ary2 := ary1;
An omitted OPTIONAL USES or MODIFIES $INPLACEARRAY parameter is passed an inplace ARRAY with all elements cleared. An omitted OPTIONAL MODIFIES or PRODUCES inplace ARRAY is discarded upon return. An explicit default value (e.g., using OPTIONAL(v)) is not allowed for inplace ARRAY parameters.
The parameter qualifier $REFERENCE is allowed for inplace ARRAYs, although it is not allowed for simple data types. A $REFERENCE parameter is effectively an alias for its argument; see Section 7.5.4.
INTEGER $INPLACEARRAY(1 TO 10) ary1,ary2,ary3;
. . .
write(logFile,(IF procTrue THEN ary1 EL ary3)[2],eol,
(IF procFalse THEN ary2 EL ary3)[4],eol);
clear(ary) clears all elements of a sized inplace ARRAY ary.
The pseudofield operations, such as ary.lb1 and ary.$ub2, can also be applied to sized inplace ARRAYs; they are computed at compiletime.
INIT inplaceArray (...) is not currently supported.
$expr(inplace ARRAY expression) is not currently supported.
The comparison operations = and NEQ are not currently supported on inplace ARRAYs.
If a CLASS cls has an inplace ARRAY field ary, it is not currently possible to access a constant-valued pseudofield of cls.ary (e.g., cls.ary.ub1), despite the fact that the compiler ought to be able to figure out the pseudofield's value at compiletime. You must instead declare an inplace record r of cls or declare and allocate a POINTER p to cls and use r.ary.ub1 or p.ary.ub1.
If a CLASS cls has an inplace ARRAY field ary, it is not currently possible to compute a displacement using DSP and cls.ary (e.g., DSP(cls.ary[3])). You must instead use an expression like:
DSP(cls.ary) + (3 - lower bound of ary) * element size of ary
12.9.3.1. Inplace ARRAYs and the Foreign Language Interface
Currently, inplace ARRAYs can be passed to or from a foreign language only as $REFERENCE parameters (see Section 7.5.4).
There is currently no way to specify the $ALIGN qualifier for an inplace ARRAY as for the CLASS of an inplace record. In most cases, this means you should not pass an inplace ARRAY local or outer variable to a foreign language, but instead pass only inplace ARRAYs that are fields of aligned records, e.g.:
$ALIGN CLASS classWithInplaceArray (
LONG REAL $INPLACEARRAY (0 TO 999) ary;
);
$RECORD(classWithInplaceArray) rec;
...
foreignProc(rec.ary);
12.10.1. Dynamic ARRAY Assignment
One dynamic
ARRAY may be assigned to another if they are assignment compatible
(see Section 4.9), i.e., if they are of the same data type
(except that either could be untyped)
and dimension (except that either could be
dimensionless), and corresponding constant bounds are the same.
Dynamic ARRAY assignment does not copy elements.
Instead, both ARRAYs are made to point to the same dynamic ARRAY
object,
and so refer to the same elements.
Figure 12–2 illustrates
the pointer nature of dynamic ARRAYs.
It is the programmer's responsibility to ensure that a dynamic ARRAY assignment makes sense. It is possible to write syntactically correct ARRAY assignments that are logically invalid and therefore have undefined effects. For example, given the following declarations:
ARRAY arr1;
INTEGER ARRAY(1 TO 6) arr3;
INTEGER ARRAY(1 TO 2,1 TO 3) arr4;
arr1 is assignment compatible with both arr3 and arr4, but arr3 and arr4 are not assignment compatible with each other, since they have different dimensions. The following assignments are accepted by the compiler since each assignment involves assignment compatible ARRAYs:
arr1 := arr3; arr4 := arr1
The effect is the same as assigning arr3 to arr4, which would be invalid since arr3 and arr4 are not assignment compatible. The effect of the statements above is therefore undefined. In particular, this approach does not allow the same ARRAY to be accessed as both a one- and a two-dimensional ARRAY, since the data structures to which the ARRAY POINTERs point are different.
12.10.1.1. Copying Elements from One Dynamic ARRAY to Another
Dynamic ARRAY assignment does not copy ARRAY elements.
To get an effect with typed,
constant-bounded dynamic ARRAYs similar to that of assigning
inplace ARRAYs to one another, use $ref
(see Section 7.5.4.5):
STRING ARRAY(1 TO 10) ary1,ary2;
# Assume ary1 is not NULLARRAY:
ary2 := ary1; # ary1 and ary2 point at the
# same dynamic object now
new(ary1);
$ref(ary1) := $ref(ary2);
# ary1 and ary2 point at
# different objects now, but
# ary1's elements are a copy
# of ary2's
The INIT statement may be used to initialize a dynamic ARRAY. The ARRAY must be allocated with new before it is initialized. Untyped ARRAYs and inplace ARRAYs cannot be initialized with the INIT statement.
The form of the INIT statement is:
INIT v (c1, ..., cn)
where v is a dynamic ARRAY variable and the ci are initialization specifiers. The simplest form of initialization specifier is a constant expression of v's data type. The ith initialization value (after application of any replications, as described below) initializes the ith element of v. An example of an INIT statement for a one-dimensional dynamic ARRAY:
STRING ARRAY(1 TO 3) cmds;
new(cmds); INIT cmds ("view","clear","next");
The programmer is responsible for ensuring that multidimensional ARRAYs are properly initialized. ARRAYs are stored with the last dimensions varying most rapidly. For example, a two-dimensional ARRAY is stored by rows (first row immediately followed by second row, and so forth). For example, an ARRAY declared as:
type ARRAY(1 TO 3, 1 TO 4) a
has its elements stored in the order:
a[1,1], a[1,2], a[1,3], a[1,4], a[2,1], a[2,2],
a[2,3], a[2,4], a[3,1], a[3,2], a[3,3], a[3,4].
A two-dimensional ARRAY can be initialized with an INIT statement as follows:
INTEGER ARRAY(1 TO 3, 1 TO 4) arr3;
new(arr3); INIT arr3 (2,8,7,5,3,9,8,7,1,3,5,7);
Table 12–3 shows what the initialized ARRAY arr3 would look like when viewed as a matrix.
Table 12–3. Array arr3 as a Matrix
| Column 1 | Column 2 | Column 3 | Column 4 | |
|---|---|---|---|---|
| Row 1 | 2 | 8 | 7 | 5 |
| Row 2 | 3 | 9 | 8 | 7 |
| Row 3 | 1 | 3 | 5 | 7 |
An error occurs if there are more initialization values than elements of v. There may be fewer initialization values than elements of v, in which case any elements for which an initial value is not specified are set to Zero.
An ARRAY may be initialized with the INIT statement many times. The INIT statement is executed each time it is encountered.
An initialization specifier may consist of a bracketed INTEGER constant expression (a “replication”) followed by an initialization value. The replication specifies the number of consecutive elements the initialization value is to initialize. If the replication is less than or equal to zero, the initialization value is ignored. For example, this code:
INTEGER ARRAY(1 TO 4, 1 TO 2) arr4;
new(arr4);
INIT arr4 ([3] 8, [2] 7, 9, [2] 6)
would initialize arr4 as:
| Column 1 | Column 2 | |
|---|---|---|
| Row 1 | 8 | 8 |
| Row 2 | 8 | 7 |
| Row 3 | 7 | 9 |
| Row 4 | 6 | 6 |
A garbage collection may occur during the execution of an INIT statement.
It is common to allocate and initialize an ARRAY in the INITIAL PROCEDURE (see Section 11.11) and deallocate it in the FINAL PROCEDURE (see Section 11.12).
The ARRAYs are considered equal
if and only if they point at the same ARRAY object,
and thus share the same elements. In other words, an
ARRAY comparison compares just the ARRAY POINTERs.
To check whether two distinct ARRAYs
have the same element values, use an iterative statement
(Section 5.8) to compare corresponding elements one by one.
12.10.4. Converting a POINTER to an ARRAY and Vice Versa
Since a dynamic ARRAY variable
is just a POINTER to a dynamic ARRAY object,
it is legal to convert a dynamic ARRAY to an unclassified
POINTER and vice versa.
The conversion PROCEDURE cvp(ary) returns an unclassified POINTER to the object ary points to.
The conversion PROCEDURE cvAry(p,ary) sets a dynamic ARRAY variable ary to point to the same object p points to. Currently, no check is made whether the conversion is correct (except on SUPERCHECK platforms—see Chapter 18); the effects are undefined if the object p points to is not an ARRAY that matches ary's declaration.
In both cvp and cvAry,
the conversion is purely syntactic; no elements are copied and no
storage is allocated.
12.10.5. Miscellaneous
12.10.5.1. Copying a Dynamic ARRAY
The system PROCEDURE copy (see Section 32.54)
may be used to copy elements
from one ARRAY to another.
12.10.5.2. Clearing a Dynamic ARRAY
The system PROCEDURE clear can clear (set to Zero) any number of
elements of an ARRAY. For example, if an
ARRAY called arr1 has 50
elements, then clear(arr1) clears the entire ARRAY, and
clear(arr1,20) clears the first 20 elements of arr1.
An ARRAY is automatically cleared when it is allocated by new.
12.11. Deallocating Inplace ARRAYs
Inplace ARRAYs are deallocated automatically when
the PROCEDURE frame or object that contains them goes away.
Inplace ARRAYs cannot become garbage.
12.12. Deallocating Dynamic ARRAYs
The system PROCEDURE dispose
is used to deallocate ARRAYs.
dispose(arr1)
allows the storage associated with arr1 to be immediately reused.
No check is made that there are no other POINTERs
currently pointing to the same object as arr1.
If there are such POINTERs, they become dangling;
using them to access data has undefined effects.
The use of dispose is therefore inherently unsafe, although judicious use of dispose can improve performance in many programs by reducing garbage collection time. A facility called POINTER rigging (see Chapter 6 of the MAINDEBUG User's Guide) can sometimes make it easier to track dangling POINTER bugs.
It is not necessary to dispose an ARRAY explicitly, since an
ARRAY that becomes inaccessible
(no longer pointed to by any accessible POINTER)
is automatically collected by the
MAINSAIL garbage collector;
the only reason to call dispose is to improve program performance.
12.13. How ARRAYs Are Stored in Memory
The elements of an ARRAY are stored in memory in the order
described in Section 12.10.2.
There may be padding between ARRAY elements only for
inplace ARRAYs of records;
see Section 10.13.
12.14. Unsized Inplace ARRAYs and Unsized CLASSes
The upper bound of the first dimension of an inplace ARRAY can be
declared as *, in which case the inplace ARRAY is unsized.
An inplace
ARRAY that is not unsized is called
sized. An unsized inplace ARRAY is
allowed only as the last field of a
CLASS or as a $REFERENCE parameter
(see Section 7.5.4).
A CLASS with an unsized inplace ARRAY field is called an unsized CLASS.
If c is an unsized CLASS with an unsized inplace ARRAY field ary, e.g.:
CLASS c (
INTEGER i;
REAL $INPLACEARRAY(-1 TO *) ary;
);
then:
An unsized CLASS c cannot be used:
An unsized inplace ARRAY cannot be used in any operation that depends on its size, such as ARRAY assignment (the individual elements of the ARRAY may, of course, be assigned). ary.ub1 or ary.$ub1 is a compiletime error for an unsized inplace ARRAY.
Even if CHECK is in effect, the first subscript of an unsized inplace ARRAY cannot be checked against the undeclared upper bound. The use of unsized ARRAYs is therefore inherently unsafe, and should be considered a “low-level” MAINSAIL feature.
A record of an unsized CLASS cannot be used in any operation that depends on its size. For example, records of an unsized CLASS cannot be assigned, and cannot be passed as USES, MODIFIES, or PRODUCES parameters. (Such records can be passed as $REFERENCE parameters.) Records of an unsized CLASS cannot be elements of ARRAYs (inplace or dynamic). Note that POINTERs to records of an unsized CLASS are allowed, as are inplace or dynamic ARRAYs of such POINTERs.
12.14.1. Parameterized Records
A parameterized record is a special case of an unsized record that can
be dynamically allocated with more than zero elements in its unsized
ARRAY field.
A parameterized record is a record of a
parameterized record CLASS c, which is defined as follows:
c is an unsized CLASS (its last field is an unsized inplace ARRAY; call it ary in the examples below).
A parameterized record can be allocated with a specified number of elements for ary by using the following instance of the system PROCEDURE new:
POINTER(c)
PROCEDURE new (CLASS c; LONG INTEGER numElements;
OPTIONAL POINTER($area) areaRec);
c must be a parameterized record CLASS. numElements is used for the number of elements of ary; 0L is allowed. new(c) is treated like new(c,0L).
$parameterizedRecord includes a LONG INTEGER field, $parameterizedRecordElements, which is set to the number of elements of the unsized inplace ARRAY field when a record is allocated with the above instance of new.
Example:
CLASS($parameterizedRecord) cls (
INTEGER i,j,k;
STRING r,s;
INTEGER $INPLACEARRAY(5 TO *) ary);
POINTER(cls) p;
p := new(cls,10L); # allocate cls with 10 elements for ary
# p.$parameterizedRecordElements = 10L
p.ary[i] := 0;
# if CHECK is in effect, i GEQ 5 is checked, but currently
# not i LEQ 14
The effect of modifying the $parameterizedRecordElements field of a parameterized record is undefined.
Note that although the upper bound of the unsized ARRAY could be computed from $parameterizedRecordElements and used for bounds checking of ARRAY subscripts, MAINSAIL does not currently do so; however, it may do so in the future.
The following PROCEDURE can be used to allocate a parameterized record of an anonymous CLASS:
POINTER($parameterizedRecord)
PROCEDURE $createParameterizedRecord
(POINTER p;
LONG INTEGER numElements;
OPTIONAL POINTER($area) areaRec);
p must point to a record or a CLASS descriptor; a record of the specified CLASS is allocated. It is an error if the CLASS is not a parameterized record CLASS.
The following PROCEDURE returns TRUE if and only if p points to a parameterized record:
BOOLEAN
PROCEDURE $isParameterizedRecord
(POINTER p);
The POINTER form of the system PROCEDURE size, size(p), returns the amount of space occupied by p's record, whether or not it is a parameterized record.