Structured Programming in CUnit 313 min read
Functions in C: Definition, Types, Calls, Scope & Recursion
Unit 3 of Structured Programming in C covers how to define, declare, and call functions in C, including parameter passing (call by value/reference), scope rules, recursion, and library functions—with visual traces, real-world examples, and code snippets for TU/PU exam readiness.
TAKEAWAYS:
- Functions in C are reusable code blocks that reduce redundancy and improve modularity by breaking programs into logical units.
- Call by value passes a copy of the argument (safe but cannot modify original), while call by reference passes the memory address (allows modification but risks side effects).
- Scope rules determine where variables are accessible: local (block-level), global (file-level), and formal parameters (function-level).
- Recursion is a function calling itself to solve problems like factorial or Fibonacci, but requires a base case and proper stack management to avoid infinite loops.
- Library functions (e.g.,
printf(),sqrt()) are pre-written functions in headers like<math.h>or<stdio.h>that save development time. - Prototypes (function declarations) must precede calls to ensure the compiler knows the function’s signature before execution.
1. What is a Function?
A function is a self-contained block of code that performs a specific task. It:
- Takes input (arguments/parameters).
- Processes the input.
- Returns an output (or modifies data via references).
- Can be reused across a program.
Why Use Functions?
mindmap
root((Why Use Functions?))
Modularity["Breaks code into logical units (e.g., `calculateSalary()`)"]
Reusability["Same function used in multiple places (e.g., `validateInput()`)"]
Maintainability["Easier to debug/test individual functions"]
Abstraction["Hides complex logic behind simple interfaces"]2. Types of Functions in C
C functions are classified based on:
- Return Type: Whether they return a value or not.
- Arguments: Whether they take parameters or not.
| Type | Syntax | Example | Use Case |
|---|---|---|---|
| No return, no args | void functionName(void) |
void greet(void) |
Display a welcome message. |
| Return, no args | returnType functionName(void) |
int getRandom(void) |
Generate a random number. |
| No return, with args | void functionName(type arg) |
void printName(char name[]) |
Print a user’s name. |
| Return, with args | returnType functionName(type arg) |
int add(int a, int b) |
Calculate sum of two numbers. |
Example:
#include <stdio.h>
// Function prototype (declaration)
int multiply(int, int);
int main() {
int result = multiply(5, 3); // Function call
printf("Result: %d", result);
return 0;
}
// Function definition
int multiply(int a, int b) {
return a * b;
}
3. Call by Value vs. Call by Reference
A. Call by Value
- A copy of the argument is passed.
- Changes inside the function do not affect the original variable.
- Safe but cannot modify the original data.
Example: Swapping Two Numbers (Fails)
#include <stdio.h>
void swap(int x, int y) {
int temp = x;
x = y;
y = temp;
}
int main() {
int a = 5, b = 10;
swap(a, b); // Passes copies of a and b
printf("a = %d, b = %d", a, b); // Output: a = 5, b = 10 (no change)
return 0;
}
Trace Table:
| Step | a (main) |
b (main) |
x (swap) |
y (swap) |
|---|---|---|---|---|
| Before swap | 5 | 10 | 5 | 10 |
| After swap | 5 | 10 | 10 | 5 |
B. Call by Reference (Using Pointers)
- The memory address of the argument is passed.
- Changes inside the function affect the original variable.
- Risky if misused (can corrupt data).
Example: Swapping Two Numbers (Works)
#include <stdio.h>
void swap(int *x, int *y) {
int temp = *x;
*x = *y;
*y = temp;
}
int main() {
int a = 5, b = 10;
swap(&a, &b); // Passes addresses of a and b
printf("a = %d, b = %d", a, b); // Output: a = 10, b = 5
return 0;
}
Trace Table:
| Step | a (main) |
b (main) |
*x (swap) |
*y (swap) |
|---|---|---|---|---|
| Before swap | 5 | 10 | 5 | 10 |
| After swap | 10 | 5 | 10 | 5 |
4. Scope of Variables in Functions
Variables in C have limited visibility based on their declaration:
| Scope | Declaration | Accessibility | Example |
|---|---|---|---|
| Local (Block) | Inside a function/block | Only within that block | int x = 10; inside main() |
| Global (File) | Outside all functions | Entire file (can cause naming conflicts) | int globalVar; at top of file |
| Formal Parameter | Function arguments | Only within the function | void func(int param) |
Example: Scope Rules
#include <stdio.h>
int global = 10; // Global scope
void display() {
int local = 20; // Local scope
printf("Global: %d, Local: %d", global, local);
}
int main() {
int mainLocal = 30;
display(); // Output: Global: 10, Local: 20
printf("Main local: %d", mainLocal); // Output: 30
return 0;
}
Mermaid Diagram: Variable Scope Hierarchy
classDiagram
class Global {
+Accessible everywhere
}
class Local {
+Accessible only in block/function
}
class Parameter {
+Accessible only in function
}
Global --> Local : "Can access local if passed"
Local --> Parameter : "Cannot access unless returned"5. Recursion in C
Recursion is a function calling itself to solve problems iteratively. It must have:
- A base case (stopping condition).
- A recursive case (function calls itself with modified input).
stateDiagram-v2
[*] --> fact:5
fact:5 --> fact:4 : n=5
fact:4 --> fact:3 : n=4
fact:3 --> fact:2 : n=3
fact:2 --> fact:1 : n=2
fact:1 --> fact:0 : n=1
fact:0 --> |base case| [*]
note right of fact:5
Call stack grows until
base case (n=0/1)
end noteRecursive calls for factorial(5) with base case terminationExample: Factorial of a Number
#include <stdio.h>
int factorial(int n) {
if (n == 0 || n == 1) // Base case
return 1;
else
return n * factorial(n - 1); // Recursive case
}
int main() {
int num = 5;
printf("Factorial of %d is %d", num, factorial(num));
return 0;
}
Trace Table:
| Step | n |
factorial(n) Call Stack |
Output |
|---|---|---|---|
| 1 | 5 | 5 * factorial(4) |
- |
| 2 | 4 | 5 * 4 * factorial(3) |
- |
| 3 | 3 | 5 * 4 * 3 * factorial(2) |
- |
| 4 | 2 | 5 * 4 * 3 * 2 * factorial(1) |
- |
| 5 (Base) | 1 | 5 * 4 * 3 * 2 * 1 = 120 |
120 |
Real-World Example:
- Pathao’s Ride Allocation: Recursion can model nested decision trees for matching drivers to passengers based on location and time.
- Nepal Stock Exchange (NEPSE) Price Prediction: Recursive algorithms (e.g., backtracking) can simulate "what-if" scenarios for stock prices.
6. Library Functions in C
C provides pre-written functions in header files (e.g., <math.h>, <stdio.h>). Examples:
| Header | Function | Purpose | Example |
|---|---|---|---|
<math.h> |
sqrt(x) |
Square root of x |
printf("%f", sqrt(16)); |
<stdio.h> |
printf() |
Print formatted output | printf("Hello %s", name); |
<stdlib.h> |
rand() |
Generate random number | int num = rand() % 100; |
<string.h> |
strlen(str) |
Length of a string | int len = strlen("C"); |
Example: Using pow() from <math.h>
#include <stdio.h>
#include <math.h>
int main() {
double base = 2, exponent = 3;
double result = pow(base, exponent);
printf("%g^%g = %g", base, exponent, result); // Output: 2^3 = 8
return 0;
}
7. Function Prototypes (Declarations)
- Prototypes tell the compiler about a function before it is called.
- Prevents errors by ensuring the function exists and has the correct signature.
- Syntax:
returnType functionName(type1 arg1, type2 arg2, ...);
Example: Prototypes in Action
#include <stdio.h>
// Prototype (declaration)
int add(int, int);
int main() {
int sum = add(5, 7); // Call before definition
printf("Sum: %d", sum);
return 0;
}
// Definition (can be after main)
int add(int a, int b) {
return a + b;
}
Why Use Prototypes?
- Improves readability (functions can be defined later).
- Helps the compiler catch errors early.
In the Real World
eSewa’s Payment Processing
- Uses modular functions to handle:
validateUser()(checks credentials).processPayment()(deducts amount from wallet).sendReceipt()(sends confirmation via SMS).
- Call by reference ensures the user’s balance is updated in the database.
- Uses modular functions to handle:
Khalti’s Transaction Logging
- Recursion is used in audit trails to log nested transactions (e.g., a merchant’s refund request triggers a chain of
verifyTransaction()calls).
- Recursion is used in audit trails to log nested transactions (e.g., a merchant’s refund request triggers a chain of
Daraz’s Order Queue System
- Functions manage:
enqueueOrder()(adds a new order to a queue).dequeueOrder()(processes the next order in FIFO order).
- Call by value ensures order details are copied safely before processing.
- Functions manage:
Ncell’s Billing Calculation
calculateBill()uses library functions likepow()to compute roaming charges:double roamingCharge = baseRate * pow(1.1, minutesUsed);
Exam Tip
Function Definition vs. Declaration:
- Declaration (prototype) is
returnType funcName(type args);. - Definition includes the body
{ ... }. - Example Question: "Write a prototype for a function
int checkPrime(int num)."
- Declaration (prototype) is
Call by Value/Reference:
- Always trace variable changes in exams. Show how original values remain unchanged (call by value) or modify (call by reference).
- Common Mistake: Forgetting
&in call by reference.
Recursion:
- Must include:
- Base case (e.g.,
if (n == 0) return 1;). - Recursive case (e.g.,
return n * factorial(n-1);).
- Base case (e.g.,
- Exam Question: "Write a recursive function to print numbers from
nto1."
- Must include:
Library Functions:
- Memorize common headers (
<math.h>,<stdio.h>,<string.h>) and their functions. - Example: "What header must be included to use
strlen()?" →<string.h>.
- Memorize common headers (
Scope Rules:
- Global variables can be accessed anywhere but should be avoided unless necessary.
- Local variables are preferred for better maintainability.
Practice Question (TU-style):
"Write a C program to find the greatest of three numbers using functions. Use call by value for input and call by reference to return the result."
Solution:
#include <stdio.h>
void findGreatest(int a, int b, int c, int *result) {
*result = (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);
}
int main() {
int num1 = 5, num2 = 9, num3 = 2, greatest;
findGreatest(num1, num2, num3, &greatest);
printf("Greatest: %d", greatest); // Output: 9
return 0;
}
In the real world
- eSewa: Uses recursion in its transaction validation system to verify nested merchant permissions (e.g., checking if a sub-merchant has approval from parent merchants).
- Ncell’s billing system: Employs call by reference when updating customer data in real-time (e.g., modifying a user’s plan via pointers to avoid redundant database reads).
- Pathao’s ride-matching algorithm: Leverages function prototypes (declarations before use) to modularize steps like driver location checks, fare calculations, and ride confirmation—ensuring the compiler knows each function’s signature before execution.
Based on the TU BITM syllabus for Structured Programming in C (ITM102), unit 3.
Discussion
Loading…