Introduction
A function packages a computation behind a name and a list of parameters. Functions make programs readable, let code be reused, and are the mechanism behind recursion. For GATE, the important questions are what happens to the arguments: which changes made inside a function are visible to the caller, and what the call stack looks like while functions are active.
C has exactly one parameter-passing mechanism: call by value. Call by reference is simulated by passing pointers. Other mechanisms (call by reference, value-result, name) belong to other languages but are tested through pseudocode, so they get a section of their own here.
Conventions: C11, LP64 (pointers are 8 bytes).
Prerequisites
- Control Flow: Selection and Loops: statements and
return. - Pointers: the address-of and indirection operators, needed for call by reference in C. The essentials are repeated where used.
Core concepts
Declaration, definition and prototype
int max(int a, int b); /* declaration with a prototype */
int max(int a, int b) { /* definition */
return a > b ? a : b;
}- A prototype is a declaration that lists the parameter types. With a prototype in scope, each argument is converted to its parameter type, and the number of arguments is checked.
- Call a function only after a declaration of it is visible. Since C99, calling an undeclared function violates a constraint and requires a diagnostic; gcc 14 rejects it. (C89 allowed it, assuming the function returned
int.) int f(void)declares a function with no parameters.int f()declares a function with unspecified parameters (an obsolescent form in C11); calls to it receive no checking and the default argument promotions are applied.- The return type may be
void. A function cannot return an array or a function, but it can return a structure by value, or a pointer.
Terminology
- Formal parameters (parameters) are the names in the definition:
aandbinmax. - Actual parameters (arguments) are the expressions in the call: in
max(x + 1, y), the arguments arex + 1andy.
Call by value in C
At a call, each argument expression is evaluated (in an unspecified order), converted to the parameter type, and copied into a fresh parameter object that belongs to the called function. The function works on the copy. Assigning to a parameter never changes the caller's variable.
void swap_wrong(int a, int b) { int t = a; a = b; b = t; }
int x = 3, y = 7;
swap_wrong(x, y); /* x is still 3, y is still 7 */Simulating call by reference with pointers
To let a function change a caller's variable, pass its address. The pointer itself is still passed by value, but the function can reach the original object through it.
void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; }
swap(&x, &y); /* now x is 7, y is 3 */The same idea explains scanf("%d", &n): scanf needs the address of n to store into it.
Arrays as arguments
An array argument is converted to a pointer to its first element (array-to-pointer conversion). The parameter declarations int a[], int a[10] and int *a are identical: all declare a pointer. Consequences:
- Changes to
a[i]inside the function change the caller's array. - Inside the function
sizeof aissizeof(int *), which is 8 under LP64, not the size of the array. gcc warns about this. The length must be passed separately.
void fill(int a[], int n) { for (int i = 0; i < n; i++) a[i] = i * i; }
int arr[5]; fill(arr, 5); /* arr becomes 0 1 4 9 16; sizeof arr in main is 20 */Structures are copied
A structure argument is copied in full; changes to its members inside the function are not seen by the caller. A structure can also be returned by value. For large structures, passing a pointer (often const struct T *) avoids the copy.
static local variables
A local variable declared static is created once, before the program starts, and keeps its value between calls:
int counter(void) { static int c = 0; return ++c; }The third call returns 3. Storage classes are covered in Scope, Storage Classes and Linkage.
main and its parameters
int main(int argc, char *argv[]) receives the number of command-line words in argc and the words in argv[0] to argv[argc - 1]; if argc > 0, argv[0] is a string representing the program name (possibly empty); guard argc > 0 before accessing argv[0] as a string, and argv[argc] is a null pointer. Running ./prog alpha beta gives argc = 3, argv[1] = "alpha".
How it works: the call stack
Each call creates an activation record (stack frame), usually on a run-time stack that grows as calls nest and shrinks as they return. A typical frame holds:
- the arguments (or space for them, depending on the calling convention: x86-64 passes the first six integer arguments in registers),
- the return address, where execution resumes in the caller,
- the saved frame pointer of the caller (a control link),
- the function's automatic local variables and temporaries.
The most recent call is always at the top, so calls finish in last-in, first-out order: this is why recursion can be implemented with a stack.
int g(int c) { int d = c * 10; return d + 1; }
int f(int a) { int b = a + 1; int r = g(b); return r * 2; }
int main(void) { int m = f(2); }While g runs, three frames exist: main (with m not yet assigned), f (with a = 2, b = 3) and g (with c = 3, d = 30). g returns 31, f returns 62, and main stores m = 62.

g runs in the chain main calls f(2) calls g(3): one frame per active call, each with its parameters, return address and locals; the stack top is g.The full compiler view (access links, displays, calling sequences) is in Runtime Environments.
Lifetime consequence. An automatic local object's lifetime ends when execution leaves its associated scope (normally on return). Returning its address leaves an invalid pointer; dereferencing it after that lifetime is undefined behaviour. This does not apply to static-duration local objects, which remain alive for the whole execution (Undefined, Unspecified and Implementation-Defined Behaviour).
Parameter-passing mechanisms in general
Pseudocode may say "assume call by reference" or "call by name". The mechanisms differ in what is bound to the formal parameter.
| Mechanism | Formal parameter is bound to | Caller sees changes? | Notes |
|---|---|---|---|
| Call by value | a copy of the argument's value | no | C, Java primitives |
| Call by reference | the argument's location (an alias) | yes, immediately | language-independent location model; C++ non-const T& normally needs a suitable lvalue, while const T& may bind a temporary |
| Call by value-result (copy-in copy-out) | a copy, copied back to the argument's location on return | yes, on return | aliasing makes the copy-back order matter |
| Call by name | the argument expression, re-evaluated at every use in the caller's environment | yes | Algol 60; like textual substitution without name capture |
Worked example 1: aliasing
Global x = 1; procedure p(a, b) { a = a + 1; b = b + a; }; the call is p(x, x). Final value of x:
- Value: the copies change;
xstays 1. - Reference:
aandbboth aliasx.a = a + 1makesx2;b = b + amakesx. Result 4. - Value-result: copies
a = 1, b = 1; thena = 2,b = 1 + 2 = 3; on returnais copied tox, thenbis copied tox(left to right). Result 3. With right-to-left copy-back the result would be 2, so a question must state the order. - Name: every use of
aorbmeansx, exactly as with reference here. Result 4.
Worked example 2: when name differs from reference
Global i = 1, array A[1..3] = 10, 20, 30; procedure q(u) { i = i + 1; u = u + 5; }; the call is q(A[i]).
- Value: nothing in
Achanges;ibecomes 2. - Reference:
uis bound toA[1]at the call. Afteri = i + 1,u = u + 5still updatesA[1], givingA = 15, 20, 30. - Value-result: the location
A[1]is fixed at call time, so the copy-back also updatesA[1]:A = 15, 20, 30. - Name:
umeans the expressionA[i], re-evaluated at each use. Afteribecomes 2,u = u + 5meansA[2] = A[2] + 5, givingA = 10, 25, 30.
More on mechanisms and scope rules: Parameter Passing and Static vs Dynamic Scope.
Pseudocode binding convention. A reference argument denotes a caller location when an assignable variable is supplied. Any temporary for an expression argument belongs only to an explicitly stated pseudocode model; it is not a general rule for non-const C++ references. C itself passes pointer values by value, not by reference.
Important properties and observations
- C passes every argument by value; a pointer argument lets the callee modify the pointee, not the caller's pointer variable.
- Array parameters are pointers;
sizeofinside the callee gives the pointer size. - Structures are copied on call and on return.
- The order in which arguments are evaluated is unspecified.
- A
staticlocal keeps its value across calls and is initialised once. - Returning a pointer to an automatic local object whose lifetime ends on leaving its scope creates a dangling pointer; static-duration locals are excluded.
- To change a caller's pointer (for example, the head of a linked list), pass a pointer to the pointer (Linked Lists).
GATE examination perspective
- Swap questions. Decide whether values or addresses are passed; only the pointer version swaps.
- Mechanism tables. For "value, reference, value-result, name" questions, trace each mechanism separately and watch for aliasing (
p(x, x)) and for array arguments whose index changes inside the procedure. - Static locals. Count calls; the variable is not reset.
- Stack depth. The maximum number of simultaneously active frames equals the maximum nesting depth of calls, which for recursion is the recursion depth.
sizeofof an array parameter is the pointer size, a common pitfall.
Summary
Sources
- ISO/IEC 9899:2011 (C11), committee draft N1570, (consulted 2026-11-09): Section 5.1.2.2.1 (program startup, argc and argv), 6.5.2.2 (function calls), 6.7.6.3 (function declarators, prototypes and array parameter adjustment), 6.9.1 (function definitions).
- B. W. Kernighan and D. M. Ritchie, The C Programming Language, 2nd ed., Prentice Hall, 1988: Chapter 4 (functions and program structure) and Section 5.2 (pointers and function arguments).
- A. V. Aho, M. S. Lam, R. Sethi, J. D. Ullman, Compilers: Principles, Techniques, and Tools, 2nd ed., Addison-Wesley, 2007: Section 1.6 (programming language basics), in particular 1.6.6 (parameter passing mechanisms) and 1.6.7 (aliasing).