Arrays

Explicit (1 TO 5) bounds are clearer than OPTION BASE and work in every dialect.

13.1 Declaring arrays

DIM a(n) gives indexes 0..n (n + 1 elements) unless OPTION BASE 1 is in effect.

Accessing an index outside the bounds raises error 9, Subscript out of range.

In QBasic, using an undeclared array like a(5) implicitly creates a(0 TO 10). Always DIM explicitly.

DIM scores(9) AS INTEGER          ' 10 elements: indexes 0 to 9
DIM names(1 TO 5) AS STRING       ' explicit bounds: 1 to 5
DIM temps(-10 TO 10) AS SINGLE    ' any integer bounds
names(1) = "Ada": names(2) = "Grace": names(3) = "Linus"
FOR i = 1 TO 5
    PRINT i; names(i)
NEXT

13.2 OPTION BASE

Explicit (1 TO 5) bounds are clearer than OPTION BASE and work in every dialect.

OPTION BASE 1          ' must appear before any arrays are dimensioned
DIM v(5)               ' now 1 to 5

13.3 LBOUND, UBOUND and passing arrays

Arrays are always passed by reference; pass them with empty parentheses: values(). (Note that DATA, WIDTH, NAME, POS, and LOC are keywords and cannot be used as variable names.)

DIM values(1 TO 8) AS DOUBLE
FOR i = LBOUND(values) TO UBOUND(values): values(i) = RND * 100: NEXT
PRINT "Average:"; Average#(values())
PrintArray values()
END

FUNCTION Average# (a() AS DOUBLE)
    DIM i AS LONG, total AS DOUBLE
    FOR i = LBOUND(a) TO UBOUND(a)
        total = total + a(i)
    NEXT
    Average# = total / (UBOUND(a) - LBOUND(a) + 1)
END FUNCTION

SUB PrintArray (a() AS DOUBLE)
    DIM i AS LONG
    FOR i = LBOUND(a) TO UBOUND(a)
        PRINT USING "###.##  "; a(i);
    NEXT
    PRINT
END SUB

13.4 Multidimensional arrays

Typical uses: game boards, tile maps, matrices, spreadsheets, images.

CONST ROWS = 3, COLS = 4
DIM grid(1 TO ROWS, 1 TO COLS) AS INTEGER
FOR r = 1 TO ROWS
    FOR c = 1 TO COLS
        grid(r, c) = r * 10 + c
    NEXT
NEXT
FOR r = 1 TO ROWS
    FOR c = 1 TO COLS
        PRINT USING "####"; grid(r, c);
    NEXT
    PRINT
NEXT
PRINT "Dimension 2 bounds:"; LBOUND(grid, 2); "to"; UBOUND(grid, 2)

13.5 Static vs. dynamic arrays, REDIM and _PRESERVE

Static arrays have fixed size decided at compile time (constant bounds with DIM).

Dynamic arrays can be resized at run time with REDIM. Arrays declared with REDIM, with variable bounds, or after the '$DYNAMIC metacommand are dynamic.

REDIM alone clears all elements; REDIM _PRESERVE keeps them (QB64; in QuickBASIC PDS 7.1 it was REDIM PRESERVE).

Growing one element at a time is slow for large arrays — grow by doubling instead and keep a separate count.

With multidimensional arrays, _PRESERVE reliably preserves data only when the last dimension changes.

REDIM items(0) AS STRING           ' start with a dynamic array
DIM count AS LONG
DO
    LINE INPUT "Item (blank to finish): "; t$
    IF t$ = "" THEN EXIT DO
    count = count + 1
    REDIM _PRESERVE items(1 TO count) AS STRING   ' grow, keeping existing contents
    items(count) = t$
LOOP
FOR i = 1 TO count: PRINT i; items(i): NEXT

INPUT "How many values"; n
REDIM values(1 TO n) AS DOUBLE      ' REDIM without _PRESERVE erases the contents
ERASE values                        ' frees a dynamic array (clears a static one)

13.6 Common array algorithms

Classic BASIC: DIM A(100), DIM B$(20), numeric arrays only up to a couple of dimensions in some 8-bit BASICs; no REDIM. Arrays could only be dimensioned once ("Redim'd array" error). FreeBASIC: Dim a(1 To 10) As Integer, ReDim Preserve a(1 To 20), LBound/UBound, array initializers Dim primes(0 To 4) As Integer = {2, 3, 5, 7, 11}, dynamic arrays Dim a() As Integer. VB.NET: arrays are zero-based objects: Dim a(9) As Integer (10 elements), Dim b() As Integer = {1, 2, 3}, ReDim Preserve a(19), a.Length, Array.Sort(a); usually prefer List(Of T).

' Find the maximum and its index
best = LBOUND(a)
FOR i = LBOUND(a) + 1 TO UBOUND(a)
    IF a(i) > a(best) THEN best = i
NEXT

' Reverse in place
lo = LBOUND(a): hi = UBOUND(a)
DO WHILE lo < hi
    SWAP a(lo), a(hi)          ' SWAP exchanges two variables of the same type
    lo = lo + 1: hi = hi - 1
LOOP

' Shuffle (Fisher-Yates)
RANDOMIZE TIMER
FOR i = UBOUND(a) TO LBOUND(a) + 1 STEP -1
    j = LBOUND(a) + INT(RND * (i - LBOUND(a) + 1))
    SWAP a(i), a(j)
NEXT