Function syntax takes five minutes to memorise, but output prediction, recursion traces, and failed swaps still cost marks. Functions are where C's call-by-value rule, call stack, and storage classes collide. That collision is what the exam charges you for: swapVal leaves its caller untouched, sizeof on an array parameter reports 8 bytes instead of 20, and fib(5) burns 15 calls to return a single digit.
What a function is: declaration, definition, and call
Every C function involves three parts. A declaration, or prototype, gives the signature before first use. A definition supplies the body. A call transfers control to it and can receive the result.
In int add(int a, int b) { return a + b; }, int is the return type, add the name, (int a, int b) the parameter list, the braces the body, and return the returned value. A void return type sends no value. (void) means no arguments.
int add(int, int);
int add(int a, int b) {
return a + b;
}
int r = add(3, 4);The call computes 3 + 4 = 7, so r = 7. The prototype lets the compiler check the call. Older compilers could make implicit assumptions without one and mishandle a floating-point argument. A missing prototype is a defect, not a style nit.
Parameter passing: call by value vs call by "reference"
C is always pass-by-value. Each argument is copied. "Call by reference" in C means copying a pointer that still leads to the caller's variable.
void swapVal(int a, int b) {
int t = a; a = b; b = t;
}
void swapRef(int *a, int *b) {
int t = *a; *a = *b; *b = t;
}
int x = 5, y = 9;
swapVal(x, y);
swapRef(&x, &y);After swapVal(x, y), x = 5 and y = 9. Its private copies changed from a = 5, b = 9 to a = 9, b = 5, then disappeared. Starting again from x = 5, y = 9, swapRef(&x, &y) dereferences the addresses and writes into main. The result is x = 9, y = 5.
If a function must change the caller's variable, pass its address, not merely its value.

Passing arrays and strings: the pointer-decay trap
An array passed to a function decays to a pointer to its first element. The function cannot recover its length, so pass that separately.
int sum(int arr[], int n) {
int s = 0;
for (int i = 0; i < n; i++) s += arr[i];
return s;
}
int a[5] = {2, 4, 6, 8, 10};
int total = sum(a, 5);The accumulator moves through 0 + 2 = 2, 2 + 4 = 6, 6 + 6 = 12, 12 + 8 = 20, and 20 + 10 = 30. Therefore, sum(a, 5) = 30.
Inside sum, sizeof(arr) gives the pointer size, 8 bytes on a 64-bit machine in this example, not the 20 bytes occupied by five 4-byte integers. Thus, sizeof(arr) / sizeof(arr[0]) gives the wrong count. Strings decay the same way, but a string carries its own terminator, so strlen(s) walks to the \0 and needs no separate length. That holds only while the terminator survives. On a five-character source, strncpy(d, s, 5) fills d completely and writes no \0, so a later strlen(d) reads past the end.
Scope, lifetime, and storage classes
Scope says where a name is visible. Lifetime says how long its storage exists. An ordinary local has block scope and is recreated per call. A static local has block scope but whole-program lifetime.
Storage class | Meaning |
|---|---|
| Default local storage, recreated on each call |
| Persists across calls, or restricts a file-scope name to its source file |
| Declares a global object or function defined elsewhere |
| Requests fast access as a hint, rarely useful with modern compilers |
int counter(void) {
static int c = 0;
c++;
return c;
}Three calls return 1, then 2, then 3. The single c is initialised once and retains its value. With int c = 0;, automatic storage makes the calls return 1, 1, 1. A static local remembers state without a global.
Recursion: how the call stack actually works
Recursion is a function calling itself. It needs a base case that stops and a recursive case moving towards it. A missing base case pushes frames until stack overflow.
int fact(int n) {
if (n <= 1) return 1;
return n * fact(n - 1);
}For fact(4), calls descend through fact(3), fact(2), and fact(1). The base gives fact(1) = 1. Unwinding gives fact(2) = 2 * 1 = 2, fact(3) = 3 * 2 = 6, and fact(4) = 4 * 6 = 24. The final answer is 24.
void f(int n) {
if (n == 0) return;
f(n - 1);
printf("%d ", n);
}
void g(int n) {
if (n == 0) return;
printf("%d ", n);
g(n - 1);
}f(3) prints 1 2 3 while calls return. g(3) prints 3 2 1 before each deeper call. Moving printf across the recursive call flips the order.
This same call-stack pattern drives recursive tree traversal. Binary Trees and Binary Search Trees is the natural next read for recursion over a data structure.

When recursion gets expensive: the DP bridge
Naive recursion can repeat the same work many times.
int fib(int n) {
if (n < 2) return n;
return fib(n - 1) + fib(n - 2);
}The sequence 0, 1, 1, 2, 3, 5 gives fib(5) = 5. Let C(n) count calls. Then C(0) = C(1) = 1 and C(n) = 1 + C(n - 1) + C(n - 2). Thus C(2) = 3, C(3) = 5, C(4) = 9, and C(5) = 15. The same values, including fib(3) and fib(2), are recomputed.
Memoisation stores each answer so every subproblem is solved once. Dynamic Programming Explained: 0/1 Knapsack develops that bridge. Deep recursion can overflow the stack, while divide-and-conquer sorts such as mergesort and quicksort use recursion by design.
How GATE and interviews test functions
Typical question shapes are predictable:
Predict recursive output. The
fandgpair trains the print-order trap.Trace the call stack. The factorial trace shows both descent and return.
Find a static local's value after several calls. The counter gives the pattern.
Decide whether a function changed caller state. The two swaps expose the value-copy rule.
Reason about a function pointer passed as a callback. A parameter declared
int (*op)(int, int)holds a copied function address, soop(3, 4)withopset toaddcallsaddand yields7.
Functions and recursion belong to Programming and Data Structures, as the official GATE syllabus specifies. Interviews may ask for swapping without a temporary variable, recursion versus iteration, or the meaning of static in context. The GATE CS Subject Weightage breakdown shows where these ideas sit among the surrounding subjects.
The short version and your next step
C passes by value. Pass a pointer to modify caller state.
Arrays decay to pointers in parameters, so pass the length.
Static locals retain values across calls.
Every recursion needs a reachable base case.
Printing before versus after recursion reverses output order.
Naive recursion can be exponential, so memoise repeated subproblems.
Pointers in C for GATE traces the same addresses that make swapRef work, one memory diagram per dereference. Use the GATE CS Exam preparation courses for the full Programming and Data Structures track. If the language groundwork needs attention first, start with the CS Fundamentals courses.




