Subroutines (procedures and functions) are named blocks of code designed to perform a specific task. They are essential for breaking down large programs into manageable, reusable modules.
1. Procedures (No Return Value)
A Procedure executes a sequence of statements but does not return a value to the caller.
Syntax
A procedure is declared using the procedure keyword, followed by its name and an optional parameter list. The code is contained in a begin...end block.
procedure ShowWelcomeMessage;
begin
ShowMessage('Application started successfully.');
end;
// How to call the procedure
// ShowWelcomeMessage;
2. Functions (Returns a Value)
A Function executes a sequence of statements and is designed to compute and return a single value to the caller.
Syntax
A function is declared using the function keyword, followed by its name, parameter list, and a colon followed by the return type.
The return value is set by assigning a value to the function’s name or the dedicated Result variable within the function body. Using Result is the modern and preferred practice.
// Function that calculates the area of a circle
function CalculateArea(Radius: Double): Double;
const
Pi = 3.14159;
begin
// Set the return value using the Result variable
Result := Pi * Radius * Radius;
end;
// How to call the function
// var CircleArea: Double;
// CircleArea := CalculateArea(10.0);
3. Parameters (Passing Data)
Parameters are variables listed in the procedure or function header that allow the subroutine to accept data from the caller.
A. Pass by Value (Default)
This is the standard, default behavior. When data is passed by value, the subroutine receives a copy of the original variable’s value. Any changes made to the parameter inside the subroutine do not affect the original variable outside of it.
procedure AddOne(Value: Integer); // Default: Pass by Value
begin
Value := Value + 1; // Changes only the local copy
end;
// var A: Integer = 5;
// AddOne(A); // A is still 5 after the call.
B. Pass by Reference (var)
When a parameter is preceded by the var keyword, it is passed by reference. The subroutine receives a pointer to the original memory location, meaning changes made to the parameter do affect the original variable.
This is primarily used to allow a procedure/function to modify the caller’s variables directly, or when passing large data structures (like records or large strings) to save time and memory by avoiding a copy.
procedure AddOneReference(var Value: Integer); // Pass by Reference
begin
Value := Value + 1; // Changes the original variable
end;
// var B: Integer = 5;
// AddOneReference(B); // B is 6 after the call.
4. Forward Declarations
In Object Pascal, a subroutine must be declared before it is called. If two subroutines call each other (mutual recursion or circular dependency), the first one called must be declared before the second.
To resolve this, you can use a forward declaration in the interface section of a unit:
- Place the full signature (name, parameters, return type) followed by a semicolon and the keyword
forwardin theinterfacesection. - Provide the full implementation later in the
implementationsection.
// In the INTERFACE section:
procedure ProcA(Value: Integer); forward;
procedure ProcB(Value: Integer); // ProcB doesn't need 'forward' if ProcA is defined above
// In the IMPLEMENTATION section:
procedure ProcA(Value: Integer);
begin
// Calls ProcB before ProcB's code appears in implementation
ProcB(Value - 1);
end;
procedure ProcB(Value: Integer);
begin
// Code...
end;
5. Scope
Scope defines the region of the program where an identifier (variable, procedure, or function name) is accessible.
- Global Scope: Identifiers declared directly in the unit’s
interfacesection are visible to all other units that use this unit. - Unit Scope: Identifiers declared directly in the unit’s
implementationsection are only visible within that unit. - Local Scope: Variables declared inside the
varblock of a procedure or function are only visible within that subroutine.
