ITM102 Structured Programming in C

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.
123main()calculateSalary()validateInput()displayResult()
Function call hierarchy in a salary calculation program (modularity example)

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:

  1. Return Type: Whether they return a value or not.
  2. Arguments: Whether they take parameters or not.
void greet(void)0int getRandom(void)1void printName(char name[])2int add(int a, int b)3
Four function types with their signatures (no args/return vs. with args/return)
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

[object Object][object Object][object Object]TOP
Call stack during swap operations (call by value vs. 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:

  1. A base case (stopping condition).
  2. 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 note
Recursive calls for factorial(5) with base case termination

Example: 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

  1. 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.
  2. 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).
  3. 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.
  4. Ncell’s Billing Calculation

    • calculateBill() uses library functions like pow() to compute roaming charges:
      double roamingCharge = baseRate * pow(1.1, minutesUsed);
      

Exam Tip

  1. 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)."
  2. 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.
  3. Recursion:

    • Must include:
      • Base case (e.g., if (n == 0) return 1;).
      • Recursive case (e.g., return n * factorial(n-1);).
    • Exam Question: "Write a recursive function to print numbers from n to 1."
  4. 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>.
  5. 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…