1.1 What are Subroutines?
A subroutine is a named, self-contained block of code that performs a specific task. Once defined, it can be called (invoked) from anywhere in the program — meaning you write the logic once and reuse it many times.
Real-world analogy: think of a recipe book. Each recipe has a name ("Make Pancakes") and a list of steps. When you want pancakes, you do not re-write the recipe from scratch — you just follow it. A subroutine works the same way: give the block of code a name, then call that name whenever you need the task done.
Why use subroutines?
- Reusability: write the code once, call it from many places.
- Avoid code duplication: no need to copy and paste the same logic over and over — change it in one place and every call benefits.
- Modularity: break a large program into smaller, manageable pieces.
- Readability: well-named subroutines act as abstractions — the reader sees what is being done without needing to read the details.
- Easier debugging & testing: each subroutine can be tested on its own before being joined into the full program.
CIE 0478 pseudocode has two kinds of subroutine:
Procedure
Performs an action (e.g. prints a menu, draws a shape, displays a message) but does not return a value.
CALL Greet("Sam")
Function
Performs a task and returns exactly one value via the RETURN statement.
Total <- Add(3, 4)
Key idea: the choice between a procedure and a function is determined by whether you need a value back. If the subroutine's job is to do something (print, draw, display), use a procedure. If its job is to compute something (sum, max, average), use a function.
What are Subroutines?
1.2 Procedures: Declaring & Calling
A procedure is declared between PROCEDURE and ENDPROCEDURE. It is invoked with the CALL keyword.
Syntax:
PROCEDURE <Name>(<Parameter1> : <Type>, <Parameter2> : <Type>, ...)
<statements>
ENDPROCEDURE
// Calling the procedure:
CALL <Name>(<Argument1>, <Argument2>, ...)Example 1 — a parameterless procedure that greets the user:
PROCEDURE Greet()
OUTPUT "Hello, and welcome to the program!"
OUTPUT "This is a procedure with no parameters."
ENDPROCEDURE
// Main program
CALL Greet()Example 2 — a procedure with one parameter that prints a row of stars:
PROCEDURE PrintStars(Count : INTEGER)
DECLARE Line : STRING
DECLARE I : INTEGER
Line <- ""
FOR I <- 1 TO Count
Line <- Line & "*"
NEXT I
OUTPUT Line
ENDPROCEDURE
// Main program
CALL PrintStars(5)
CALL PrintStars(10)When CALL PrintStars(5) runs, the value 5 is passed into the parameter Count. The loop runs 5 times, building the string "*****", which is then printed. The second call prints "**********" (10 stars). The same procedure body does both jobs — that is the power of parameters.
Example 3 — a procedure with two parameters that prints an average:
PROCEDURE PrintAverage(Num1 : REAL, Num2 : REAL)
DECLARE Avg : REAL
Avg <- (Num1 + Num2) / 2
OUTPUT "The average is ", Avg
ENDPROCEDURE
// Main program
DECLARE A, B : REAL
INPUT A
INPUT B
CALL PrintAverage(A, B)- Parameters are optional: empty parentheses
()are allowed when the procedure needs no input (seeGreet()above). - No RETURN statement: a procedure never uses RETURN to send back a value. That is the job of a function.
- Always call with CALL: writing
Greet()without CALL is wrong for a procedure in 0478 pseudocode.
Warning: a procedure call is a statement, not an expression. You cannot write X <- MyProc(5) because MyProc returns nothing. Use CALL MyProc(5) as a standalone line, or change the procedure into a function if you need a value back.
Procedures: Declaring & Calling
1.3 Functions: Declaring, Calling & RETURN
A function is declared between FUNCTION and ENDFUNCTION. It must declare the type of value it returns (using RETURNS) and must contain at least one RETURN statement.
Syntax:
FUNCTION <Name>(<Parameter1> : <Type>, ...) RETURNS <ReturnType>
<statements>
RETURN <Value> // mandatory — sends Value back to the caller
ENDFUNCTION
// Calling the function — use it in an expression (NOT CALL!):
DECLARE Result : <ReturnType>
Result <- <Name>(<Argument1>, ...)Example 1 — a function that adds two integers:
FUNCTION Add(Num1 : INTEGER, Num2 : INTEGER) RETURNS INTEGER
DECLARE Sum : INTEGER
Sum <- Num1 + Num2
RETURN Sum
ENDFUNCTION
// Main program
DECLARE Result : INTEGER
Result <- Add(3, 4)
OUTPUT Result // Outputs: 7Example 2 — a function that returns a BOOLEAN (true/false):
FUNCTION IsEven(Num : INTEGER) RETURNS BOOLEAN
IF Num MOD 2 = 0 THEN
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ENDFUNCTION
// Main program — a function can be used directly in a condition:
DECLARE X : INTEGER
INPUT X
IF IsEven(X) THEN
OUTPUT X, " is even"
ELSE
OUTPUT X, " is odd"
ENDIFExample 3 — a function that returns a STRING (a letter grade):
FUNCTION GetGrade(Score : INTEGER) RETURNS STRING
IF Score >= 90 THEN
RETURN "A*"
ELSEIF Score >= 80 THEN
RETURN "A"
ELSEIF Score >= 70 THEN
RETURN "B"
ELSE
RETURN "C"
ENDIF
ENDFUNCTION
// Main program
DECLARE Mark : INTEGER
DECLARE Grade : STRING
INPUT Mark
Grade <- GetGrade(Mark)
OUTPUT "Your grade is ", GradeThe RETURN statement in detail. Inside a function, RETURN does three things at once:
- Sends one value back to the caller — exactly one value, of the type declared after
RETURNS. - Immediately ends the function — any code after the RETURN on the same execution path is never run.
- Hands a value to wherever the function was called from. The returned value can be used in three common ways: printed directly, stored in a variable, or used in a condition.
The three common ways to use a function call (its returned value):
// 1. Use the returned value directly in an OUTPUT statement:
OUTPUT Add(3, 4) // Outputs: 7
// 2. Store the returned value in a variable for later use:
DECLARE Total : INTEGER
Total <- Add(2, 3) + Add(4, 5) // Total = 5 + 9 = 14
// 3. Use the returned value directly in a condition (IF):
IF IsEven(X) THEN
OUTPUT "X is even"
ENDIF
IF Add(Price, Tax) > 100 THEN
OUTPUT "Expensive"
ENDIF- Mandatory RETURN: every function must execute a RETURN that sends back a value of the declared type. Forgetting RETURN is an error.
- Exactly one value: a function returns exactly one value. To produce two answers at IGCSE level, the usual approach is to write two separate functions (e.g.
CalcTotal()andCalcAverage()). - No CALL keyword: functions are called by writing their name and arguments in an expression. The result is then used like any other value.
- Multiple RETURN points are allowed (see
IsEvenandGetGradeabove) — the first RETURN that runs ends the function immediately.
Pattern: a function is a question with a single answer. The caller asks "what is Add(3, 4)?" and the function replies "7" via RETURN. The reply can then be printed, stored, or used in a condition — but it can only be ONE value.
Functions: Declaring, Calling & RETURN
1.4 Procedures vs Functions: When to Use Which
Knowing whether to use a procedure or a function is a key IGCSE skill. The rule is simple: do you need a value back from the subroutine, or just an action?
Comparison table — Procedure vs Function
| Feature | Procedure | Function |
|---|---|---|
| Returns a value? | No | Yes — exactly one |
| Uses RETURN? | No | Yes (mandatory) |
| Keyword to call | CALL | Used in an expression (no CALL) |
| Header keyword | PROCEDURE | FUNCTION ... RETURNS <type> |
| Used in an assignment? | No — use as a standalone CALL | Yes — e.g. X <- f(...) |
| Typical use | Print a menu, draw a shape, display a message | Calculate a total, check a condition, find a maximum |
Use a PROCEDURE when…
- You want an action performed (print, draw, display).
- No value needs to come back to the caller.
- Example:
CALL PrintMenu()
Use a FUNCTION when…
- You need a value back to use later.
- Example: calculating, checking, converting.
- Example:
Total <- CalcTotal()
Two common student errors to avoid:
- Writing
CALL Add(3, 4)for a function. Functions are never called with CALL — use the function name in an expression instead. - Writing
Result <- PrintMenu()for a procedure. Procedures return no value — there is nothing to assign. UseCALL PrintMenu()as a standalone line.
Procedures vs Functions: When to Use Which
1.5 Parameters & Arguments
- Parameter (formal): the named variable listed in the subroutine header — e.g. in
PROCEDURE Greet(Name : STRING),Nameis the parameter. - Argument (actual): the concrete value supplied at the call site — e.g. in
CALL Greet("Sam"),"Sam"is the argument.
Matching rules — the arguments in a call must match the parameters:
- By count: a subroutine with 2 parameters must be called with exactly 2 arguments.
- By position: the first argument goes to the first parameter, the second to the second, and so on. Order matters!
- By type: an INTEGER argument must go to an INTEGER parameter, a STRING to a STRING parameter, and so on.
PROCEDURE PrintAverage(Num1 : REAL, Num2 : REAL)
OUTPUT "Average is ", (Num1 + Num2) / 2
ENDPROCEDURE
// Correct — 2 REAL arguments, in the right order:
CALL PrintAverage(10.0, 20.0)
// WRONG — too few arguments:
// CALL PrintAverage(10.0)
// WRONG — too many arguments:
// CALL PrintAverage(10.0, 20.0, 30.0)Passing by value at IGCSE 0478 level. When you call a subroutine at IGCSE level, the value of each argument is copied into the matching parameter. The subroutine works on its own private copy. Changes to the parameter inside the subroutine do not affect the caller's variable.
Trace example — proving the caller's variable is unchanged:
PROCEDURE Double(X : INTEGER)
X <- X * 2 // changes the local COPY of X
OUTPUT "Inside Double, X = ", X
ENDPROCEDURE
// Main program
DECLARE Num : INTEGER
Num <- 5
CALL Double(Num)
OUTPUT "Back in main, Num = ", NumTrace — calling Double(Num) where Num = 5
Notice that the main program's variable Num is still 5 after the call. The procedure doubled its private copy X but left the caller's variable untouched. This is the safe behaviour you get from passing by value.
At IGCSE level you do not need to write any special keyword — parameters are passed by value by default. (Allowing a subroutine to actually change the caller's variable is a feature called "by reference" which is an AS Level topic — not needed for 0478.)
Parameters & Arguments
1.6 Putting It Together + Common Pitfalls
Full worked program. Here is a complete IGCSE-style program that uses BOTH a function and a procedure together. The program asks the user for three test scores, uses a function to find the highest, and uses a procedure to print a formatted result.
// ===== Function: returns the largest of three integers =====
FUNCTION Max(A : INTEGER, B : INTEGER, C : INTEGER) RETURNS INTEGER
DECLARE Highest : INTEGER
Highest <- A
IF B > Highest THEN
Highest <- B
ENDIF
IF C > Highest THEN
Highest <- C
ENDIF
RETURN Highest
ENDFUNCTION
// ===== Procedure: prints a formatted result line =====
PROCEDURE PrintResult(Name : STRING, MaxScore : INTEGER)
OUTPUT Name, "'s highest score is ", MaxScore
ENDPROCEDURE
// ===== Main program =====
DECLARE S1, S2, S3 : INTEGER
DECLARE Top : INTEGER
OUTPUT "Enter three test scores:"
INPUT S1
INPUT S2
INPUT S3
Top <- Max(S1, S2, S3) // Call the FUNCTION — its result is stored in Top
CALL PrintResult("Alex", Top) // Call the PROCEDURE — it prints the result lineLine-by-line walkthrough (assume the user enters 60, 75, 90):
FUNCTION Max(...)declares a function that takes three integers and returns one integer. TheRETURNstatement is mandatory.PROCEDURE PrintResult(...)declares a procedure that takes a name and a score and prints them. It has no RETURN.- The main program declares 4 variables, then prompts for and reads three scores into
S1,S2,S3. Top <- Max(S1, S2, S3)is a function call used in an assignment — no CALL keyword. InsideMax,Higheststarts as 60 (A), becomes 75 (B is greater), then 90 (C is greater). 90 is RETURNed and stored inTop.CALL PrintResult("Alex", Top)is a procedure call using CALL. The procedure prints "Alex's highest score is 90".
Common IGCSE pitfalls & exam tips:
- Forgetting RETURN in a function — the function declares
RETURNS INTEGERbut never executes a RETURN statement. Always check that your function ends with a RETURN that sends back the right type of value. - Mismatched parameter count — calling
Add(3)when the function expects two parameters. Count the parameters in the header and match them in the call. - Using CALL with a function — writing
CALL Add(3, 4)for a function. Functions are called by using them in an expression, not with CALL. - Trying to use a procedure's "result" — writing
X <- PrintMenu()wherePrintMenuis a procedure. Procedures return nothing, so there is no result to assign. UseCALL PrintMenu()instead. - Not declaring the function before calling it — in 0478 pseudocode the subroutine is usually declared first, then called afterwards in the main program. Declaring it after the call (where it is needed before it exists) is bad practice and may confuse the examiner.
- Confusing parameter order — calling
Subtract(3, 10)when you meantSubtract(10, 3). Arguments are matched to parameters by position, so the order matters even when the types match. Read the header carefully before writing the call.
"What's wrong with this code?" — a debug example:
FUNCTION AddTen(A : INTEGER) RETURNS INTEGER
DECLARE Result : INTEGER
Result <- A + 10
ENDFUNCTION
DECLARE X : INTEGER
X <- AddTen(5)
OUTPUT XProblem: the function header declares RETURNS INTEGER, but the body never executes a RETURN. The function has no value to give back.
Fix: add RETURN Result just before ENDFUNCTION.
// Fixed version:
FUNCTION AddTen(A : INTEGER) RETURNS INTEGER
DECLARE Result : INTEGER
Result <- A + 10
RETURN Result // <-- the missing line, now added
ENDFUNCTION
DECLARE X : INTEGER
X <- AddTen(5) // X gets 15
OUTPUT X // Outputs: 15Key points summary — a quick recap of everything you need to remember about Procedures, Functions & RETURN at IGCSE level:
Remember: a procedure performs an action (called with CALL); a function computes and returns one value (used in an expression, must contain RETURN). Parameters are the variables in the header; arguments are the values you pass when calling. At IGCSE level parameters are passed by value, so the caller's variables stay safe. Ready for the AS Level page? Head to the next topic for BYVALUE, BYREF and global scope.
Putting It Together + Common Pitfalls
1.7 Local vs Global Variables
Every variable in a pseudocode program has a scope — the part of the program where the variable exists and can be used. At IGCSE 0478 there are two kinds:
Local variable
Declared inside a procedure or function using DECLARE. It only exists while that subroutine is running. As soon as the subroutine ends, the local variable disappears.
Global variable
Declared outside any procedure or function — usually at the top of the main program. It exists for as long as the program is running and can be used anywhere, including inside procedures and functions.
Example — a global TaxRate shared by the whole program, and a local Total inside the function:
// --- Global variable (declared outside any procedure or function) ---
DECLARE TaxRate : REAL
TaxRate <- 0.15
// --- Function uses TaxRate (global) and Total (local) ---
FUNCTION CalculateTax(price : REAL) RETURNS REAL
DECLARE Total : REAL // local to this function
Total <- price * TaxRate // TaxRate is visible here (global)
RETURN Total
ENDFUNCTION
// --- Main program ---
DECLARE Amount : REAL // global to the main program
Amount <- CalculateTax(200)
OUTPUT "Tax = ", Amount
// OUTPUT Total <- ERROR: Total does not exist out hereQuick comparison:
| Feature | Local variable | Global variable |
|---|---|---|
| Declared | Inside a procedure or function | Outside any procedure or function |
| Lifetime | Only while the subroutine runs | For the whole time the program runs |
| Visible to | Only the subroutine it is declared in | The whole program, including all subroutines |
| Best for | Temporary working values (counters, totals) | Values that many parts of the program must share |
| Risk | None — kept safely inside the subroutine | Easy to change by mistake from anywhere |
Exam tip: use local variables whenever you can. They keep each procedure and function self-contained, which makes the code easier to read, test and reuse. Only use a global variable when more than one part of the program genuinely needs to share it.
Local vs Global Variables
1.8 Subroutines with Built-in Functions
The 0478 pseudocode comes with several built-in functions you can use inside your own procedures and functions: LENGTH, SUBSTRING, LCASE, UCASE, ROUND and MOD. Here are worked examples that combine these built-ins with subroutines — exactly the kind of task that appears in Paper 2.
Example 1 — a procedure that validates a password using LENGTH:
PROCEDURE ValidatePassword(password : STRING)
IF LENGTH(password) >= 8 THEN
OUTPUT "Valid"
ELSE
OUTPUT "Invalid"
ENDIF
ENDPROCEDURE
CALL ValidatePassword("mypassword") // Outputs: Valid
CALL ValidatePassword("abc") // Outputs: InvalidExample 2 — a procedure that counts vowels using LENGTH, SUBSTRING and LCASE:
PROCEDURE CountVowels(word : STRING)
DECLARE Count : INTEGER
DECLARE Ch : CHAR
Count <- 0
FOR i <- 1 TO LENGTH(word)
Ch <- LCASE(SUBSTRING(word, i, 1))
IF Ch = "a" OR Ch = "e" OR Ch = "i" OR Ch = "o" OR Ch = "u" THEN
Count <- Count + 1
ENDIF
NEXT i
OUTPUT "Vowels: ", Count
ENDPROCEDURE
CALL CountVowels("Computer") // Outputs: Vowels: 3Example 3 — a function that returns the first letter in upper case using UCASE and SUBSTRING:
FUNCTION FirstLetter(word : STRING) RETURNS CHAR
RETURN UCASE(SUBSTRING(word, 1, 1))
ENDFUNCTION
DECLARE initial : CHAR
initial <- FirstLetter("moshik")
OUTPUT "Initial = ", initial // Outputs: Initial = MExample 4 — a procedure that checks even/odd using MOD:
PROCEDURE CheckEvenOdd(num : INTEGER)
IF num MOD 2 = 0 THEN
OUTPUT "Even"
ELSE
OUTPUT "Odd"
ENDIF
ENDPROCEDURE
CALL CheckEvenOdd(7) // Outputs: Odd
CALL CheckEvenOdd(8) // Outputs: EvenExample 5 — a function with a loop that returns the sum 1 + 2 + ... + n:
FUNCTION SumToN(n : INTEGER) RETURNS INTEGER
DECLARE Total : INTEGER
Total <- 0
FOR i <- 1 TO n
Total <- Total + i
NEXT i
RETURN Total
ENDFUNCTION
DECLARE result : INTEGER
result <- SumToN(10)
OUTPUT "Sum = ", result // Outputs: Sum = 55Example 6 — a procedure with REPEAT...UNTIL validation (range check):
PROCEDURE GetMark()
DECLARE Mark : INTEGER
REPEAT
OUTPUT "Enter a mark (0-100): "
INPUT Mark
IF Mark < 0 OR Mark > 100 THEN
OUTPUT "Out of range. Try again."
ENDIF
UNTIL Mark >= 0 AND Mark <= 100
OUTPUT "Mark accepted: ", Mark
ENDPROCEDURE
CALL GetMark()Pattern: built-in functions like LENGTH, SUBSTRING, LCASE/UCASE, ROUND and MOD can be used inside your own procedures and functions. This is how real IGCSE exam tasks work — you write a subroutine that uses the built-ins to do the job and (for functions) RETURN the result.
Subroutines with Built-in Functions
1.9 Board Exam Style Question
The big scenario question at the end of Paper 2 — it tests whether you can combine functions, validation, iteration and a menu into one working program.
Question: A program allows users to convert an amount of money from USD to one of three currencies. The three conversions and their fixed rates are:
- USD → EUR: amount × 0.92
- USD → GBP: amount × 0.79
- USD → JPY: amount × 149.50
Write a program that outputs a menu to ask the user to select a conversion type from the three options, or to stop the program. The program must validate the menu input. It inputs the amount in USD, uses one function for each conversion to calculate and return the converted amount, and outputs the result rounded to two decimal places. The program continues until the user selects stop. You must use pseudocode, add comments, declare all variables and constants, and include suitable messages.
Model answer:
// Currency converter using one function per conversion
// --- Constant exchange rates ---
CONSTANT RateEUR = 0.92
CONSTANT RateGBP = 0.79
CONSTANT RateJPY = 149.50
// --- Function: USD to EUR ---
FUNCTION ConvertToEUR(USDAmount : REAL) RETURNS REAL
RETURN USDAmount * RateEUR
ENDFUNCTION
// --- Function: USD to GBP ---
FUNCTION ConvertToGBP(USDAmount : REAL) RETURNS REAL
RETURN USDAmount * RateGBP
ENDFUNCTION
// --- Function: USD to JPY ---
FUNCTION ConvertToJPY(USDAmount : REAL) RETURNS REAL
RETURN USDAmount * RateJPY
ENDFUNCTION
// --- Main program ---
DECLARE Choice : INTEGER
DECLARE USDAmount : REAL
DECLARE Converted : REAL
// Loop until the user selects option 4 (stop)
REPEAT
// Display the menu
OUTPUT "----- Currency Converter -----"
OUTPUT "1. USD to EUR"
OUTPUT "2. USD to GBP"
OUTPUT "3. USD to JPY"
OUTPUT "4. Stop the program"
OUTPUT "Enter your choice (1-4): "
INPUT Choice
// Validate the menu input
WHILE Choice < 1 OR Choice > 4 DO
OUTPUT "Invalid choice. Please enter 1, 2, 3 or 4: "
INPUT Choice
ENDWHILE
// If not the stop option, get the amount and convert
IF Choice <> 4 THEN
OUTPUT "Enter the amount in USD: "
INPUT USDAmount
// Call the matching function based on the menu choice
CASE OF Choice
1 : Converted <- ConvertToEUR(USDAmount)
OUTPUT "Amount in EUR: ", ROUND(Converted, 2)
2 : Converted <- ConvertToGBP(USDAmount)
OUTPUT "Amount in GBP: ", ROUND(Converted, 2)
3 : Converted <- ConvertToJPY(USDAmount)
OUTPUT "Amount in JPY: ", ROUND(Converted, 2)
ENDCASE
ENDIF
UNTIL Choice = 4
OUTPUT "Program stopped. Goodbye!"How marks are awarded — AO2 (max 9) for correct code:
- Constants declared for the three exchange rates.
- One function per conversion with a parameter for the USD amount.
- Each function uses
RETURNto send the converted value back. - Menu output and input of the user's choice.
- Validation that rejects values outside the range 1–4.
- Outer loop that repeats until the user selects the stop option.
- Selection that calls the correct function for each menu choice.
- Result rounded to two decimal places using
ROUND. - Output displayed in the main program (not inside the function).
How marks are awarded — AO3 (max 6) for quality:
- Code is fully commented (purpose of constants, functions, loop, validation).
- Meaningful identifier names throughout (
USDAmount,Converted,ConvertToEUR). - All variables and constants properly declared.
- Suitable messages for every input and output.
- Logical order — constants, then functions, then main program.
- Solution performs every requirement listed in the question.
Exam tip — the 15-mark question: 9 marks come from getting the code right (AO2) and 6 marks come from how well you write it (AO3). You can write working code and still lose 6 marks if your variable names are x, y, z, or if your code has no comments. Always declare every variable, always add comments, and always use meaningful names.