Functions and parameter passing
How a C call really works. Arguments are copies, so a function can only change the caller's data if you hand it an address, and arrays are passed as exactly that.
Why it exists
A function is a named piece of code you can reuse, but the real question is what the function can see and change. The caller and the callee are separate worlds, each with its own local variables. The main bridges are the arguments going in and the return value coming out (plus any global variable or pointed-to object both sides can reach). GATE questions on this topic check whether you know exactly what crosses that bridge: a copy of a value, or the address of something.
Prerequisites
Precise definitions
- Function definition. The return type, name, parameter list and body. Declaration (prototype) gives only the signature, so that calls can be checked before the definition:
int max(int a, int b);. - Formal parameters are the names in the definition (
x,y). Actual parameters (arguments) are the expressions in the call (a,b + 1). - Call by value. C always passes arguments by value: the value of each argument expression is computed and copied into the callee's formal parameter, which is a brand new local variable in the callee's frame. Changing the parameter changes only that copy.
- Simulating call by reference. To let a function change the caller's variable
a, the caller passes&a, the address ofa. The pointer parameter is itself a copy, but both the copy and the original point to the same object, so writing through it (*p = ...) changesa. - Arrays as parameters. Two separate rules combine. At the call, an array argument is converted to a pointer to its first element. In the definition, a parameter declared as an array is adjusted to a pointer, so
void f(int a[])is exactlyvoid f(int *a). The callee therefore receives an address, not a copy of the array, so element changes are visible to the caller. Inside the function,sizeof ais the size of a pointer, not the size of the array. - Return value.
return expr;copies the value ofexprback to the caller (converted to the declared return type). If control reaches the closing}of a non-voidfunction and the caller uses the returned value, the behaviour is undefined (mainis the exception: reaching its}returns 0). - Argument evaluation order is unspecified, and the arguments are fully evaluated (a sequence point) before the call happens.
How it works
First, plain call by value. Step through swap(a, b). Watch which variables change: only x and y in swap's own frame. When the call returns, the frame disappears and a and b are exactly as they were.
Pass by value: swap(a, b)
Real run, real addressesC source
Stack framesnewest on top
main()
Step 1
main creates a = 1 and b = 2 in its own frame.
Now pass addresses. The parameters x and y are still copies, but they are copies of pointers, and each arrow leads back into main's frame. Writing through the arrows changes a and b, and those changes survive the return.
Passing addresses: swap(&a, &b)
Real run, real addressesC source
Stack framesnewest on top
main()
Step 1
main creates a = 1 and b = 2.
In the usual implementation this is what an activation record is: a block holding the parameters, the locals and the return address, pushed when the call starts and popped when it ends. (C itself only promises the behaviour; real compilers may pass some arguments in registers or remove frames by inlining.) That is also why recursion works: every call gets its own copies.
Worked examples
EX 1swap by value
Easy#include <stdio.h>
void swap(int x, int y) {
int t = x;
x = y;
y = t;
}
int main(void) {
int a = 1, b = 2;
swap(a, b);
printf("%d %d\n", a, b);
return 0;
}Output:
1 2Why nothing changed. swap swaps its own copies. The original a and b are in main's frame and swap has no access to them. This is the animation above, with the real output.
EX 2swap through pointers
Easy#include <stdio.h>
void swap(int *x, int *y) {
int t = *x;
*x = *y;
*y = t;
}
int main(void) {
int a = 1, b = 2;
swap(&a, &b);
printf("%d %d\n", a, b);
return 0;
}Output:
2 1Why it works. &a and &b copy the addresses into x and y. *x and *y are the original variables, so the three assignments really swap them.
EX 3Arrays are passed as addresses
Medium#include <stdio.h>
void bump(int a[], int n) {
for (int i = 0; i < n; i++) a[i]++;
printf("%zu\n", sizeof a);
}
int main(void) {
int v[3] = {1, 2, 3};
bump(v, 3);
printf("%d %d %d\n", v[0], v[1], v[2]);
printf("%zu\n", sizeof v);
return 0;
}Output:
8
2 3 4
12Step 1. v decays to &v[0]. The function receives a pointer, so a[i]++ changes main's array: 2 3 4.
Step 2. Inside bump, sizeof a is the size of a pointer: 8 bytes on this machine (a GATE question gives the pointer size). In main, sizeof v is the whole array, bytes.
Lesson. From a plain pointer parameter, sizeof cannot recover the array's length, so the length is passed separately (n) or marked by a sentinel such as the '\0' that ends a string.
EX 4Which of the changes survive?
GATE level#include <stdio.h>
void f(int x, int *p, int a[]) {
x = 10;
*p = 20;
a[0] = 30;
p = &x;
}
int main(void) {
int x = 1, y = 2, a[2] = {3, 4};
f(x, &y, a);
printf("%d %d %d\n", x, y, a[0]);
return 0;
}Output:
1 20 30Step by step.
x = 10;changes the parameter copy only.main'sxstays 1.*p = 20;writes through the address ofy.ybecomes 20.a[0] = 30;writes through the array's address.a[0]becomes 30.p = &x;re-points the callee's own copy of the pointer. It changes nothing inmain, and it is not even used afterwards.
Rule. Changes to the parameter variable are lost. Changes to the object the parameter points to are kept.
EX 5Changing a pointer needs a pointer to it
GATE level#include <stdio.h>
int a = 1, b = 2;
void set_copy(int *p) {
p = &b;
}
void set_real(int **pp) {
*pp = &b;
}
int main(void) {
int *p = &a;
set_copy(p);
printf("%d ", *p);
set_real(&p);
printf("%d\n", *p);
return 0;
}Output:
1 2Step 1. set_copy receives a copy of the pointer p. Re-pointing the copy to b does not affect main's p, which still points to a: the first print is 1.
Step 2. To change what main's p points to, the function needs the address of p itself, a pointer to a pointer (int **). *pp = &b; writes b's address into main's p, so the second print is 2.
General rule. To modify a variable of type from inside a function, pass a pointer to . If is itself a pointer type, that makes it a pointer to a pointer.
Cheat box
Cheat box
- C passes everything by value. The parameter is a new local variable holding a copy.
- To change the caller's variable, pass its address and write through the pointer. To change a caller's pointer, pass a pointer to the pointer.
- Parameter changes are lost on return. Changes to the pointed-to object stay.
- An array parameter is a pointer.
sizeofit inside the function gives the pointer size, and the length must be passed separately. - The order in which arguments are evaluated is unspecified.
- Never return the address of a local. Returning a value, a static or a heap pointer is fine.
- Each call has its own frame, so recursion and re-entrancy work.
Traps and GATE patterns
Other things examiners twist:
- Passing a struct by value copies the whole struct, including any array inside it, so the callee's changes are lost. This is the one way to pass an array "by value".
- A function name that is not the operand of
&orsizeofis converted to a pointer to the function, which is how function pointers work. mainmay be called recursively in C (this is not allowed in C++), though it is rarely sensible.
Connections
Builds on
Unlocks
Across subjects
Within this subject
The frame the animation draws is exactly the activation record a compiler designs, and the rules for how parameters are handed over (by value, by reference, by value-result, by name) are compared in the parameter passing modes of runtime environments. The same machinery makes recursion possible.
PYQs
9 previous year questions from your book. I re-solved each one myself (9 by running or simulating, 0 by reasoning only); the book's key is shown next to my answer. All PYQs
Consider the following C program:
#include <stdio.h>
int g(int n) {
return (n+10);
}
int f(int n) {
return g(n*2);
}
int main() {
int sum, n;
sum=0;
for (n=1; n<=3; n++)
sum += f(n);
printf("%d", sum);
return 0;
}
The
output
of
the
given
C
program
is________________ (Answer in integer)Consider the following ANSI C function :
int SimpleFunction(int Y[], int n, int x)
{
int total = Y[0], loopIndex;
for (loopIndex=1; loopIndex<= n –1; loopIndex++)
total = x * total + y[loopIndex];
return total;
}
Let Z be an array of 10 elements with Z[i]=1, for all i
such that 0 <= i <=9
The value returned by SimpleFunction(Z, 10, 2) is
_______Consider the following C Functions.
int tob (int b, int* arr) {
int i;
for (i = 0; b >0; i++) {
if (b % 2)
arr[i] = 1;
else arr[i]=0;
b= b /2;
}
return (i);
}
The value returned by pp (3,4) is ____.Consider the following C program:
#include <stdio.h>
int jumble (int x, int y) {
x = 2 * x + y;
return x;
}
int main () {
int x=2, y=5;
y = jumble (y, x);
x = jumble (y, x);
printf ("%d \n", x);
return 0;
}
The value printed by the program is ______.The value printed by the following program is
__________.
void f(int* p, int m) {
m = m + 5;
*p = *p + m;
return;
}
void main() {
int i=5, j=10;
f(&i, j);
printf("%d", i+j);
}What does the following program print?
#include <stdio.h>
void f(int *p, int *q)
{
p = q;
*p = 2;
}
int i = 0, j = 1;
int main()
{
f(&i, &j);
printf("%d %d n", i, j);
getchar();
return 0;
}- (a)2 2
- (b)2 1
- (c)0 1
- (d)0 2
Consider the program below:
#include <stdio.h>
int fun(int n, int *f_p) {
int t, f;
if (n <= 1) {
*f_p = 1;
return 1;
}
t = fun(n - 1, f_p);
f = t + *f_p;
*f_p = t;
return f;
}
int main() {
int x = 15;
printf("%d\n", fun(5, &x));
return 0;
}
The value printed is:- (a)6
- (b)8
- (c)14
- (d)15
What is printed by the following C program?
int f(int x, int *py, int **ppz)
{
int y, z;
**ppz += 1;
z = **ppz;
*py += 2;
y = *py;
x += 3;
return x + y + z;
}
void main()
{
int c, *b, **a;
c = 4;
b = &c;
a = &b;
printf("%d", f(c, b, a));
}- (a)18
- (b)19
- (c)21
- (d)22
Consider the following C-program:
double foo(double); /* Line 1 */
int main()
{
double da, db;
// input da
db = foo(da);
}
double foo(double a)
{
return a;
}
The above code compiled without any error or
warning. If Line 1 is deleted, the above code will
show:- (a)no compile warning or error
- (b)some compiler-warnings not leading to unintended results
- (c)some compiler-warnings due to type-mismatch eventually leading to unintended results
- (d)compiler errors
Self-check
Question 1
What does the following print?
#include <stdio.h>
void inc(int n) {
n++;
}
int main(void) {
int k = 5;
inc(k);
inc(k);
printf("%d\n", k);
return 0;
}Show answerHide
inc increments its own copy, so k never changes:
5Question 2
How should the call and the function be changed so that k really becomes 7 after two calls?
Show answerHide
Pass the address and write through it: define void inc(int *n) { (*n)++; } and call inc(&k); twice. Note the brackets: *n++ would increment the pointer, not the integer, because postfix ++ binds tighter than *.
#include <stdio.h>
void inc(int *n) {
(*n)++;
}
int main(void) {
int k = 5;
inc(&k);
inc(&k);
printf("%d\n", k);
return 0;
}7Question 3
In the program below, what are the two numbers printed, and why does the first not equal the array size?
#include <stdio.h>
void show(int a[100]) {
printf("%zu ", sizeof a);
}
int main(void) {
int arr[100];
show(arr);
printf("%zu\n", sizeof arr);
return 0;
}Show answerHide
On this 64-bit machine:
8 400The parameter a is a pointer (8 bytes) and the 100 in its declaration is ignored. In main, arr is a real array of bytes.
Question 4
If a function receives int *p and executes p = p + 1;, does the caller's pointer change?
Show answerHide
No. p is the function's own copy of the pointer. Only writes made through the pointer (*p = ..., p[i] = ...) reach the caller's data. To move the caller's pointer you would need an int **.
Revision card
One-screen revision
- All C calls are by value: arguments are copied into new locals in the callee's frame.
- Want to change the caller's
T? PassT *. Want to change the caller's pointer? PassT **. - Parameter changes vanish on return; changes through a pointer persist.
- Arrays decay to pointers.
sizeofof an array parameter is the pointer size. - Argument evaluation order is unspecified.
- Do not return the address of a local variable.
References
- ·Kernighan and Ritchie, The C Programming Language, 2nd ed., Section 1.8 Call by Value, Section 4.1 Basics of Functions, Section 5.2 Pointers and Function Arguments
- ·Aho, Lam, Sethi, Ullman, Compilers: Principles, Techniques, and Tools, 2nd ed., Section 1.6.6 Parameter Passing Mechanisms
- ·ISO/IEC 9899:2011 (C11), Sections 6.3.2.1 Lvalues, arrays and function designators, 6.5.2.2 Function calls, 6.7.6.3 Function declarators, 6.8.6.4 The return statement, 6.9.1 Function definitions, 5.1.2.2.3 Program termination
Verification
The two animations replay real runs of instrumented C programs: every value and address is read from memory at each step. All C programs on this page are compiled and run by npm run verify:c. The prose, the comparison with other parameter-passing modes and the GATE-pattern claims have NOT yet had a human textbook check (the independent second pass is described at the end of these notes). SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand (long C programs were not re-read line by line: they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose). No defect found. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)