C Language track

Lesson 10 · Level 3: Advanced

Functions

Designing functions, pass-by-value and pass-by-reference, recursion

Level 3: Advanced5 min readFree lesson

A function is a named block of code. Gathering the same tasks in one place Duplication is reduced, and only one place needs to be modified when changing.

Learning Objectives

  • Know the difference between function declaration (prototype), definition, and call.
  • Distinguish between parameter passing methods (value passing, pointer passing).
  • Design termination conditions for recursive functions.
  • Understand the lifespan and visibility of the static variable.

Why split it into functions?

If you put all the code into one long main:

  • Duplicate code increases
  • Difficult to find where to fix a single bug
  • Difficult to reuse code

If you break it down into functions, like “This function calculates the average” or “This function sorts” You can handle code on a per-role basis.

Key concepts

1) 3 types of functions

/*1) Declaration (prototype) — announced above main*/
int add(int a, int b);

/*2) Definition — actual text*/
int add(int a, int b) { return a + b; }

/*3) Call*/
int s = add(3, 4);

Parameter data types, return types, and names must all match.

2) Flow of call stack

int add(int a, int b) { return a + b; }
int main(void) {
    int s = add(3, 4);   //← When main calls add
    return 0;            //main stops until add is finished
}
Start call End call
  ┌────────┐                   ┌────────┐
│ add │ ◄── currently running │ main │ ◄── currently running
  ├────────┤                   └────────┘
  │  main  │
  └────────┘
Stack (Last-In-First-Out)

When the function ends, it returns to exactly where it was called.

3) C always passes by value (call by value)

void inc(int x) { x++; }     //Increment the copy by 1 and end
int n = 5;
inc(n);                      //n remains 5

To change a variable in the caller, you must pass a pointer.

void inc_ptr(int *x) { (*x)++; }
inc_ptr(&n);                 //n is 6

4) Recursive function

long long factorial(int n) {
    if (n <= 1) return 1;             //Terminating condition (base case)
    return n * factorial(n - 1);      //call yourself
}

Stack overflow (infinite calls) if exit condition is missing or incorrect.

5) The two faces of static

  • static variable within a function: The value is maintained between calls (lifetime is the entire program).
  • static attached to the function: Can only be called within the same file (not visible to the outside).

See as example

Example 1 — ex01_basic.c: Declaration/Definition/Call in function

int add(int a, int b);
int max(int a, int b);
void print_line(void);

Execution result

--------------------
add(3, 4) = 7
max(7, 2) = 7
--------------------

Key point: the prototype is above main so the compiler knows the calling type in advance.

Example 2 — ex02_recursion.c: Factorial and Fibonacci

long long factorial(int n);
long long fib(int n);

Execution result

 0! = 1,  fib( 0) = 0
 1! = 1,  fib( 1) = 1
 2! = 2,  fib( 2) = 1
 3! = 6,  fib( 3) = 2
 4! = 24,  fib( 4) = 3
 5! = 120,  fib( 5) = 5
 6! = 720,  fib( 6) = 8
 7! = 5040,  fib( 7) = 13
 8! = 40320,  fib( 8) = 21
 9! = 362880,  fib( 9) = 34
10! = 3628800,  fib(10) = 55

Key: The termination condition (n <= 1) is the leaf of the call tree.

Example 3 — ex03_array_param.c: Array to function

int sum(const int arr[], int n) { ... }
double average(const int arr[], int n) { return (double)sum(arr,n) / n; }

Execution result

Total: 150
Average: 30.00

Bottom line: Array parameters to functions are effectively pointers. So we get the length separately.

Example 4 — ex04_static.c: Maintaining values between calls

int counter(void) {
    static int count = 0;
    count++;
    return count;
}

Execution result

call 1 -> 1
call 2 -> 2
call 3 -> 3
call 4 -> 4
call 5 -> 5

Key point: Without static, it will be initialized to 0 every time and will always output 1.

See it from a different perspective — “Function is a vending machine”

Parameter (input) Return value (output)
       ▼                              ▲
   ┌───────────────────────────┐
│ Function body (black box) │
   └───────────────────────────┘

A deterministic vending machine that gives the same output for the same input is easier to test and reuse. The fewer side effects, such as dependencies on global variables and screen output, the better.

Common Mistakes

  1. Missing return type: If you leave out the return type like add(int a, int b), old C assumes int (warning now).
  2. Call without declaration: The compiler may guess and use the wrong calling convention.
  3. Missing recursion termination condition: infinite recursion → stack overflow (runtime crash).
  4. Return of local variable address: Invalid after function ends (int *bad(void) { int x; return &x; }).
  5. sizeof of array parameter: void f(int a[10]) { sizeof(a); } result is pointer size, not array size.

Cleanup

  • Functions are tools for separating and reusing code by role.
  • The call uses the call stack and returns to the caller when finished.
  • C is for passing by value. Pass a pointer to change a variable in the caller.
  • Recursion must have an exit condition.
  • The static local variable maintains its value between calls.

Try it yourself

cd src
gcc -std=c11 -Wall -o ex01 ex01_basic.c       && ./ex01
gcc -std=c11 -Wall -o ex02 ex02_recursion.c   && ./ex02
gcc -std=c11 -Wall -o ex03 ex03_array_param.c && ./ex03
gcc -std=c11 -Wall -o ex04 ex04_static.c      && ./ex04

Application:

  • Rewrite fib of ex02 as a loop version and compare the performance.
  • Subtract static from ex04 and see how the call result changes.

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/ex01_basic.cC
#include <stdio.h>

int add(int a, int b);
int max(int a, int b);
void print_line(void);

int main(void) {
    print_line();
    printf("add(3, 4) = %d\n", add(3, 4));
    printf("max(7, 2) = %d\n", max(7, 2));
    print_line();
    return 0;
}

int add(int a, int b) { return a + b; }
int max(int a, int b) { return a > b ? a : b; }
void print_line(void) { printf("--------------------\n"); }
src/ex02_recursion.cC
#include <stdio.h>

long long factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

long long fib(int n) {
    if (n < 2) return n;
    return fib(n - 1) + fib(n - 2);
}

int main(void) {
    for (int i = 0; i <= 10; i++) {
        printf("%2d! = %lld,  fib(%2d) = %lld\n",
               i, factorial(i), i, fib(i));
    }
    return 0;
}
src/ex03_array_param.cC
#include <stdio.h>

/*Array parameters are effectively passed as pointers.*/
int sum(const int arr[], int n) {
    int s = 0;
    for (int i = 0; i < n; i++) s += arr[i];
    return s;
}

double average(const int arr[], int n) {
    return (double)sum(arr, n) / n;
}

int main(void) {
    int data[] = {10, 20, 30, 40, 50};
    int n = (int)(sizeof(data) / sizeof(data[0]));

    printf("Total: %d\n", sum(data, n));
    printf("Average: %.2f\n", average(data, n));
    return 0;
}
src/ex04_static.cC
#include <stdio.h>

int counter(void) {
    static int count = 0;   /*Values persist between calls*/
    count++;
    return count;
}

int main(void) {
    for (int i = 0; i < 5; i++) {
        printf("Call %d -> %d\n", i + 1, counter());
    }
    return 0;
}

Assignment

Problem 1

A function to recursively find the greatest common divisor (GCD) for two integers a and b. Write int gcd(int a, int b). (Euclidean algorithm)

  • File name: hw01.c

Problem 2

Function that returns maximum index from an array of integers Write int find_max_index(const int *arr, int n).

  • File name: hw02.c

Problem 3

Write a recursive function that takes a positive integer n and outputs a binary string. (printBinary(13) -> 1101)

  • File name: hw03.c
Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

hw01.cC
#include <stdio.h>

int gcd(int a, int b) {
    if (b == 0) return a;
    return gcd(b, a % b);
}

int main(void) {
    int a, b;
    printf("Two integers:");
    scanf("%d %d", &a, &b);

    printf("GCD(%d, %d) = %d\n", a, b, gcd(a, b));
    return 0;
}
hw02.cC
#include <stdio.h>

int find_max_index(const int *arr, int n) {
    int idx = 0;
    for (int i = 1; i < n; i++)
        if (arr[i] > arr[idx]) idx = i;
    return idx;
}

int main(void) {
    int arr[] = {3, 7, 2, 8, 5, 8, 1};
    int n = (int)(sizeof(arr) / sizeof(arr[0]));

    int idx = find_max_index(arr, n);
    printf("The maximum value %d is at index %d.\n", arr[idx], idx);
    return 0;
}
hw03.cC
#include <stdio.h>

void printBinary(unsigned int n) {
    if (n > 1) printBinary(n / 2);
    putchar('0' + (n % 2));
}

int main(void) {
    unsigned int n;
    printf("Enter a positive integer:");
    scanf("%u", &n);

    printBinary(n);
    putchar('\n');
    return 0;
}

Learn this lesson with a mentor

Work through the C Language track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now