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

Core concepts

Declaration, definition and prototype

C
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: a and b in max.
  • Actual parameters (arguments) are the expressions in the call: in max(x + 1, y), the arguments are x + 1 and y.

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.

C
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.

C
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 a is sizeof(int *), which is 8 under LP64, not the size of the array. gcc warns about this. The length must be passed separately.
C
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:

C
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.

C
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.

Stack frames for the call chain main, f, g
Figure 1. The run-time stack while 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.

MechanismFormal parameter is bound toCaller sees changes?Notes
Call by valuea copy of the argument's valuenoC, Java primitives
Call by referencethe argument's location (an alias)yes, immediatelylanguage-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 returnyes, on returnaliasing makes the copy-back order matter
Call by namethe argument expression, re-evaluated at every use in the caller's environmentyesAlgol 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; x stays 1.
  • Reference: a and b both alias x. a = a + 1 makes x 2; b = b + a makes x 2+2=42 + 2 = 4. Result 4.
  • Value-result: copies a = 1, b = 1; then a = 2, b = 1 + 2 = 3; on return a is copied to x, then b is copied to x (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 a or b means x, 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 A changes; i becomes 2.
  • Reference: u is bound to A[1] at the call. After i = i + 1, u = u + 5 still updates A[1], giving A = 15, 20, 30.
  • Value-result: the location A[1] is fixed at call time, so the copy-back also updates A[1]: A = 15, 20, 30.
  • Name: u means the expression A[i], re-evaluated at each use. After i becomes 2, u = u + 5 means A[2] = A[2] + 5, giving A = 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; sizeof inside the callee gives the pointer size.
  • Structures are copied on call and on return.
  • The order in which arguments are evaluated is unspecified.
  • A static local 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.
  • sizeof of an array parameter is the pointer size, a common pitfall.

Summary

Sources

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

Next steps