Parameters, Scope & Recursion

In QBasic and QB64, variables are passed by reference: the SUB works on the caller's variable itself.

12.1 By reference (the QBasic default)

In QBasic and QB64, variables are passed by reference: the SUB works on the caller's variable itself.

The argument's type must match the parameter's type exactly when passing a variable by reference; otherwise you get a "Parameter type mismatch" error.

DIM n AS INTEGER
n = 5
Increment n
PRINT n            ' 6 — the SUB changed the caller's variable
END

SUB Increment (x AS INTEGER)
    x = x + 1
END SUB

12.2 Passing by value

To protect the caller's variable, pass an expression instead of a variable — wrapping the argument in an extra pair of parentheses does this:

Alternatively, copy the parameter into a local variable inside the procedure before modifying it. In QB64, the BYVAL keyword is used in DECLARE LIBRARY blocks for external C functions (Chapter 30); for ordinary SUBs, use the techniques above.

by reference (variables), by value (expressions) BYVAL in DECLARE LIBRARY FreeBASIC (-lang fb) by value for numbers; ByRef for strings and UDTs (by convention, efficient) ByVal, ByRef, Const ByRef ByVal, ByRef by reference

n = 5
Increment (n)          ' parentheses make it an expression → a temporary copy is passed
PRINT n                ' still 5
CALL Increment((n))    ' with CALL you need double parentheses

12.3 Local variables

Variables used inside a SUB or FUNCTION are local: they are created on entry and destroyed on exit, and are independent of same-named variables in the main program.

x = 100
ShowLocal
PRINT x            ' 100 — unaffected
END

SUB ShowLocal
    x = 1          ' a different, local x
    PRINT x
END SUB

12.4 Sharing variables: DIM SHARED and SHARED

Keep shared (global) variables to a minimum: prefer parameters and return values.

DIM SHARED score AS LONG       ' visible in every SUB and FUNCTION
DIM lives AS INTEGER           ' main-module only
lives = 3
AddPoints 50
PRINT score                    ' 50
END

SUB AddPoints (p AS LONG)
    score = score + p          ' works: score is DIM SHARED
END SUB

SUB ShowLives
    SHARED lives AS INTEGER    ' share one main-module variable with this SUB only
    PRINT lives
END SUB

12.5 STATIC variables

A STATIC local variable keeps its value between calls:

SUB Name (params) STATIC makes all locals static (an old QuickBASIC speed trick).

FOR i = 1 TO 3: CountCalls: NEXT
END

SUB CountCalls
    STATIC calls AS LONG
    calls = calls + 1
    PRINT "Called"; calls; "times"
END SUB

12.6 Recursion

A procedure that calls itself must have a base case that stops the recursion.

Each call uses stack space; very deep recursion can overflow the stack. Naive Fib& recomputes the same values many times — Chapter 28 shows memoization and iterative versions.

FreeBASIC: Declare Sub Name(...) is required for procedures used before their definition; Static and Shared work as in QBasic; Dim Shared for globals. Locals can be declared in any block (Scope ... End Scope). VB.NET: module-level Private/Public fields replace DIM SHARED; Static locals exist; recursion works the same.

PRINT Factorial&&(20)          ' 2432902008176640000
PRINT Fib&(20)                 ' 6765
PRINT GCD&(1071, 462)          ' 21
CountDown 5
END

FUNCTION Factorial&& (n AS LONG)
    IF n <= 1 THEN
        Factorial&& = 1
    ELSE
        Factorial&& = n * Factorial&&(n - 1)
    END IF
END FUNCTION

FUNCTION Fib& (n AS LONG)
    IF n < 2 THEN Fib& = n ELSE Fib& = Fib&(n - 1) + Fib&(n - 2)   ' exponential time!
END FUNCTION

FUNCTION GCD& (a AS LONG, b AS LONG)       ' Euclid's algorithm
    IF b = 0 THEN GCD& = a ELSE GCD& = GCD&(b, a MOD b)
END FUNCTION

SUB CountDown (n AS INTEGER)
    IF n = 0 THEN PRINT "Liftoff!": EXIT SUB
    PRINT n
    CountDown n - 1
END SUB