ZetCode

FreeBasic Procedures

Last modified October 18, 2026

A procedure is a named block of statements that can be called from several places. FreeBasic has two kinds: a Sub performs an action and returns nothing, while a Function computes and returns a value. This article covers how to declare them, how parameters are passed, and how overloading and recursion work. The keyword articles Sub, Function, and Declare describe each statement in detail.

Declaring procedures

A procedure is declared with Sub or Function. A function also states the type of the value it returns, while a sub returns nothing. The Declare statement repeats a procedure's signature so that it can be called before its body appears in the source file. The example below declares both procedures first, then calls them, and only afterward defines their bodies.

procedures_declare.bas
Declare Function Square(x As Integer) As Integer
Declare Sub PrintSquare(n As Integer)

PrintSquare(4)
PrintSquare(7)

Sub PrintSquare(n As Integer)
    Print n; " squared is "; Square(n)
End Sub

Function Square(x As Integer) As Integer
    Return x * x
End Function

The Declare lines at the top announce the signatures of Square and PrintSquare. This lets code call a procedure before its body appears in the file. PrintSquare calls Square inside its body, and Square is defined below it. The Return statement in a function supplies its value. Each Print number is preceded by a space, which is why the output has two spaces before the squared values.

$ fbc -lang fb procedures_declare.bas
$ ./procedures_declare
 4 squared is  16
 7 squared is  49

Passing parameters by value and by reference

A parameter declared ByVal receives a copy of the argument, so changes inside the procedure do not affect the caller. A parameter declared ByRef refers to the caller's variable, so changes are visible after the call. In the fb dialect, numeric parameters without a qualifier are passed by value, while String and user-defined type parameters default to by-reference. Writing the qualifier explicitly makes the intent clear.

procedures_byval.bas
Sub AddOneByVal(ByVal n As Integer)
    n += 1
End Sub

Sub AddOneByRef(ByRef n As Integer)
    n += 1
End Sub

Dim value As Integer = 10
AddOneByVal(value)
Print "After ByVal call: "; value
AddOneByRef(value)
Print "After ByRef call: "; value

AddOneByVal increments its own copy of n, so value remains 10. AddOneByRef increments the caller's variable directly, so value becomes 11. Sub calls use the statement form without Call.

$ fbc -lang fb procedures_byval.bas
$ ./procedures_byval
After ByVal call:  10
After ByRef call:  11

Recursion

A procedure may call itself. Each call works on its own copy of the local values. A recursive procedure needs a base case that stops the recursion; without one, the calls never end.

procedures_recursion.bas
Function Factorial(n As Integer) As LongInt
    If n <= 1 Then Return 1
    Return n * Factorial(n - 1)
End Function

Print "5! = "; Factorial(5)
Print "15! = "; Factorial(15)

Factorial returns 1 when n is 1 or less, which is the base case. Otherwise it multiplies n by the factorial of n - 1. The function returns LongInt because 15! is larger than a 32-bit integer can hold on a 32-bit target; LongInt is 64 bits on every target.

$ fbc -lang fb procedures_recursion.bas
$ ./procedures_recursion
5! =  120
15! =  1307674368000

Array parameters

An array parameter is written with empty parentheses, such as values(). The procedure then accepts any integer array, and the caller passes the array with empty parentheses too. Use LBound and UBound inside the procedure so it works with any bounds.

procedures_array.bas
Sub FillArray(values() As Integer, v As Integer)
    Dim i As Integer
    For i = LBound(values) To UBound(values)
        values(i) = v
    Next
End Sub

Dim nums(0 To 3) As Integer
FillArray(nums(), 7)
Print "nums(0) = "; nums(0); ", nums(3) = "; nums(3)

FillArray loops from the lower to the upper bound of values and writes v to each element. Because array parameters are passed by reference, the caller's nums array is changed. The call FillArray(nums(), 7) passes the array and the value.

$ fbc -lang fb procedures_array.bas
$ ./procedures_array
nums(0) =  7, nums(3) =  7

Overloading

Overloading lets several procedures share one name when their parameter lists differ in type or count. The Overload keyword is written after the name in each declaration. The definitions do not repeat it. Parameter names must not be reserved words: width is reserved in FreeBasic because of the Width statement, so the example uses w and h.

procedures_overload.bas
Declare Function Calc Overload (w As Integer, h As Integer) As Integer
Declare Function Calc Overload (s As Integer) As Integer

Function Calc (w As Integer, h As Integer) As Integer
    Return w * h
End Function

Function Calc (s As Integer) As Integer
    Return s * s
End Function

Print "Rectangle: "; Calc(4, 5)
Print "Square: "; Calc(4)

The two Calc declarations differ in parameter count, so the compiler chooses the right version from the call. Calc(4, 5) selects the rectangle version and Calc(4) selects the square version.

$ fbc -lang fb procedures_overload.bas
$ ./procedures_overload
Rectangle:  20
Square:  16

Source

FreeBASIC documentation

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all FreeBasic Tutorials.