C Programming · Lesson 4 of 10 · Updated ·
Functions and Recursion in C — Prototypes, Call by Value, Storage Classes
A function is a named block of code that does one job. C programs are built from many small functions.
1. Declare, define, call
#include <stdio.h>
int square(int n); /* prototype (declaration) */
int main(void) {
printf("%d %d\n", square(4), square(square(2)));
return 0;
}
int square(int n) { /* definition */
return n * n;
}
16 16
- The prototype tells the compiler the function's name, return type and parameters before it's used.
- A function that returns nothing has return type
void.
2. Call by value (C's only way)
C copies each argument into the parameter. Changing the parameter doesn't change the caller's variable:
#include <stdio.h>
void broken_swap(int x, int y) { int t = x; x = y; y = t; }
void swap(int *x, int *y) { int t = *x; *x = *y; *y = t; }
int main(void) {
int a = 5, b = 9;
broken_swap(a, b);
printf("after broken_swap: %d %d\n", a, b);
swap(&a, &b);
printf("after swap: %d %d\n", a, b);
return 0;
}
after broken_swap: 5 9
after swap: 9 5
To let a function change the caller's variable, pass its address (a pointer). This is often called "call by reference", but technically C copies the pointer — still pass-by-value.
3. Recursion
A recursive function calls itself. It needs:
- a base case that stops, and
- a smaller step towards it.
#include <stdio.h>
long long factorial(int n) {
if (n <= 1) return 1; /* base case */
return n * factorial(n - 1); /* smaller step */
}
int fib(int n) {
if (n < 2) return n;
return fib(n - 1) + fib(n - 2);
}
int sum_digits(int n) {
if (n == 0) return 0;
return n % 10 + sum_digits(n / 10);
}
int main(void) {
printf("%lld %d %d\n", factorial(10), fib(10), sum_digits(9875));
return 0;
}
3628800 55 29
Each call gets its own stack frame. Too deep recursion → stack overflow (crash). Plain recursive fib is slow (exponential) — fine for learning, use a loop for big n.
4. Storage classes
| Class | Where it lives | Lifetime | Default value | Example use |
|---|---|---|---|---|
auto (default for locals) | stack | the function call | garbage | normal local variables |
static (local) | data segment | whole program | 0 | counting calls |
extern | data segment | whole program | 0 | a global defined in another file |
register | CPU register (a hint) | the block | garbage | loop counters (compilers ignore it today) |
static keeps its value between calls
#include <stdio.h>
void visit(void) {
static int count = 0; /* initialised once */
int normal = 0; /* reset every call */
count++; normal++;
printf("count=%d normal=%d\n", count, normal);
}
int main(void) {
visit(); visit(); visit();
return 0;
}
count=1 normal=1
count=2 normal=1
count=3 normal=1
5. Scope
- Local variables exist only inside their block
{ }. - Global variables (outside all functions) are visible everywhere in the file — use sparingly.
- An inner variable with the same name hides the outer one.
Common mistakes
- Calling a function before declaring it (add a prototype).
- Recursion without a base case → infinite recursion → crash.
- Expecting a normal (non-pointer) parameter to change the caller's variable.
Quick recap
- Prototype → definition → call;
voidfor no return value. - C copies arguments; pass addresses (pointers) to modify the caller's data.
- Recursion = base case + smaller step.
staticlocals keep their value across calls and start at 0.