Arrays
An array in fbc is a collection of elements where each element has the same type and is accessed with an index in to the array.
Example:
`
'' one dimensional array (1 index)
Dim a(1 To 10) As Integer
Print a(1) '' first element (integer)
Print a(10) '' last element (integer)
'' two dimensional array (2 indexes)
Dim b(1 To 2, 1 To 5) As Integer
Print b(1,1) '' first element (integer)
Print b(2,5) '' last element (integer)
`
Array Dimensions and Bounds
The number of dimensions refers to the number of indexes that are required to be given to access an element of an array. The number of dimensions may or may not be part of the declaration. If the number of dimensions are known at compile time within the scope that the array is used, fbc can check and error if the wrong number of indexes are specified.
The bounds of an array are the allowable minimum and maximum index values for each dimension. Accessing an array element with an index or indexes that are outside the array bounds of a dimension is undefined behaviour.
fbc can check and error if an index or indexes (access) are outside the bounds of the array when compiled with '-exx' or '-earray' compile options. If the array bounds are compile-time constant, and the array access is compile-time constant, fbc can check if an array access is outside the bounds of the array at compile time. Otherwise, the array bounds check must occur at run-time if either the bounds or the access is non-constant.
Fixed dimension versus unknown dimension
The number of dimensions may be fixed or unknown. fbc will attempt to determine the number of dimensions an array is expected to have based on declarations for the array. If fbc cannot determine the number of dimensions at compile time, the number of dimensions will become fixed on first redimension of the array at run time.
Example: fixed 2 dimension, dynamic bounds
`
Dim a(Any, Any) As Integer
ReDim a(1 To 2, 1 To 5)
`
Example: Dynamic dimension, dynamic bounds
`
Dim a() As Integer
'' then only one of on first time use ...
ReDim a(1 To 10)
ReDim a(1 To 2, 1 To 5)
ReDim a(1 To 2, 1 To 5, 1 To 3)
`
Once number of dimensions are known to fbc, within the scope of the array, fbc will error if any access to the error has wrong number of dimensions. Or if still unknown at compile time, as in the case of an array passed as argument to a procedure and resized, the number of dimensions become fixed at run time.
Fixed bounds versus Dynamic bounds
Fixed length arrays have array bounds that are known at compile-time. Dynamic (or variable length) arrays have array bounds that can be altered and resized at run-time, and may be considered unknown at compile time.
Example: fixed (constant) bounds and constant access
`
Dim a(1 To 10) As Integer
Print a(11) '' compile time array out-of-bounds
`
Example: fixed bounds and non-constant access
`
Dim a(1 To 10) As Integer
Dim i As Integer
Print a(i) '' run time array out-of-bounds
`
Example: dynamic bounds
`
Dim a(Any) As Integer '' 1 dimensional, empty
ReDim a(1 To 10) '' resized to 10 elements
Print a(11) '' run time array out-of-bounds
Print a(i) '' run time array out-of-bounds
`
Static Array versus Dynamic Array
Arrays may have static or dynamic memory allocation. The descriptor may be static or dynamic, and memory space for the data may be static or dynamic. The terms static and dynamic may be overused and so may lose meaning when describing an array. In this context static versus dynamic should not be confused with fixed-length or variable-length. In this context we are referring to how and where the array descriptor and it's associated data are allocated in memory, and to some extent the life time of the variable.
For an array descriptor to be valid, it must be initialized. An uninitialized array descriptor will almost certainly lead to undefined behaviour at run time.
The array descriptor itself may be allocated in .bss, .data, on stack or on heap, depending on the declaration of the array. Though typically not in .bss section because an array descriptor usually must be initialized to some non-zero default values to be usable.
The array's data may be located in .bss section, .data section, on stack or on heap, depending on the declaration of the array. In fbc's current implementation, the array data for variable-length arrays is always allocated on the heap (i.e. malloc()).
Array Descriptor
At compile time, fbc allocates an array descriptor to store and track information about the array.
From `./inc/fbc-int/array.bi`):
`
Const FB_MAXDIMENSIONS As Integer = 8
Type FBARRAYDIM
Dim As UInteger elements '' number of elements
Dim As Integer LBound '' dimension lower bound
Dim As Integer UBound '' dimension upper bound
End Type
Type FBARRAY
Dim As Any Ptr index_ptr '' @array(0, 0, 0, ... )
Dim As Any Ptr base_ptr '' start of memory at array lowest bounds
Dim As UInteger size '' byte size of allocated contents
Dim As UInteger element_len '' byte size of single element
Dim As UInteger dimensions '' number of dimensions
Dim As FBARRAYDIM dimTb(0 To FB_MAXDIMENSIONS-1)
End Type
`
If the number of dimensions is unknown at compile time, then the full `FB_MAXDIMENSIONS` is allocated in the `dimTb()` field. Otherwise, if the number dimensions is known at compile time, then only the number of dimensions needed are allocated. Therefore the allocated `FBARRAY` data may be smaller than the declared `FBARRAY` structure.
If an array is passed as argument to a procedure, an array descriptor is allocated. However, if the array is static, fixed length, and never passed as an argument, then all information about the array is known at compile time, including memory locations, and the allocation of a descriptor is optimized out, since all expressions involving the array are compile time constant.
The array descriptor may also be allocated at run time, as would be in the case of allocating a new UDT containing a variable-length array field member.
FBARRAY.index_ptr
Pointer to the array data `@array(0, 0, ...)`. This pointer may be outside of the actual array data as a kind of virtual pointer to use when calculating offsets using indexes in to the array.
FBARRAY.base_ptr
Pointer to the array's memory at the array's lowest bound. For variable-length arrays allocated at run time, this points to the allocated memory region (i.e. malloc)
FBARRAY.size
Total size in bytes of the array data. Size is equal to total number of elements in the array (all dimensions) multiplied by element length. i.e. `size = dimTb(0).elements * element_len + dimTb(1).elements * element_len + ...`
FBARRAY.element_len
Size in bytes of an individual element. Must be set to non-zero value.
FBARRAY.dimensions
Number of valid dimensions in the dimTb() table. A value of zero (0) indicates that dimTb() has `FB_MAXDIMENSIONS` avaiable, but the array does not yet have number of dimensions defined. On first REDIM, the number of dimensions will be set.
FBARRAY.dimTb()
dimTb() is an array of `FBARRAYDIM` to indicate the bounds of each dimension.
If the number of dimensions is unknown at compile time, then the full `FB_MAXDIMENSIONS` is allocated in the `dimTb()` field. Otherwise, if the number dimensions is known at compile time, then only the number of dimensions needed are allocated. Therefore the allocated `FBARRAY` data may be smaller than the declared `FBARRAY` structure.
FBARRAYDIM.elements
Number of elements in the dimension. i.e. `(ubound-lbound+1)`
FBARRAYDIM.lbound
Lower bound is the lowest valid index in this dimension.
FBARRAYDIM.ubound
Upper bound is the highest valid index in this dimension.