Pass by Value in C – Why inc(n) Doesn’t Change n

You call inc(n). The function clearly increments its argument. So why is n unchanged afterwards? This question from our C Programming Quiz App tests the single most important fact about C function calls: C passes everything by value — and a function that forgets this compiles cleanly, runs happily, and does nothing.

The Quiz Question

void inc(int x) { x++; }
int n = 5;
inc(n);
printf("%d", n);

What is printed by this code?

  1. 6
  2. 5
  3. Compile error
  4. Undefined behavior

The Correct Answer: 5

The parameter x is a brand-new local variable that receives a copy of n‘s value. x++ increments the copy to 6; the copy is destroyed when inc returns. The caller’s n was never touched. Verified on gcc 13.3 and Apple clang 21:

$ gcc -ansi -Wall -Wextra byvalue.c && ./a.out
5

Interestingly, clang sees through the futility at compile time:

$ clang -Wall -Wextra byvalue.c
warning: parameter 'x' set but not used [-Wunused-but-set-parameter]

The compiler is telling you the function modifies something nobody will ever read. (gcc 13.3 stays silent here — one more reason to build with more than one compiler.)

Why Each Wrong Answer Is Wrong

Why not 6?

6 is the pass-by-reference reading — true in C++ with void inc(int &x), but C has no references. To actually increment the caller’s variable in C, you pass its address and dereference:

void inc(int *x) { (*x)++; }
int n = 5;
inc(&n);
printf("%d", n);
$ ./a.out
6

Note what’s still true: the pointer itself is passed by value — the function gets a copy of the address. But a copy of an address points at the same original object, and that’s the whole trick.

Why not a compile error?

Everything is well-formed: inc takes an int, gets an int, modifies its own local parameter (perfectly legal), and returns nothing. A pointless program is not an invalid program.

Why not undefined behavior?

There’s no UB anywhere — no overflow, no invalid access, no unsequenced modification. The program’s behavior is completely defined; it’s just defined to be useless.

Parameters Are Local Variables — the Rule Behind Everything

The C standard treats each parameter as a local variable initialized with the corresponding argument’s value. Everything about C calls follows from this one rule:

  • Assigning to a parameter never affects the caller — whether it’s an int, a struct, or a pointer.
  • To let a callee modify caller state, pass a pointer to it — the idiom behind scanf(&x), and the swap function in this same quiz cluster.
  • Structs are copied wholesale (which can be expensive — another reason large structs travel by pointer).
  • Arrays are the apparent exception: an array argument decays to a pointer to its first element, so the callee can modify the elements. But it’s not an exception really — the pointer was passed by value.

Frequently Asked Questions

Is C pass by value or pass by reference?

Strictly pass by value — every argument is copied into the parameter. C has no references. “Pass by reference” is simulated by passing a pointer by value and dereferencing it inside the function.

How do I make a C function modify its argument?

Pass the variable’s address: declare the parameter as a pointer (void inc(int *x)), call with &n, and modify through the dereference ((*x)++). That changes the caller’s variable to 6 in this example.

Why does inc(n) compile if it does nothing?

Modifying your own local variable (which a parameter is) is legal C. Clang’s -Wunused-but-set-parameter warning can flag the pattern, but no rule of the language forbids it.

Related Reading

Recommended Books

This question is #52 in the C Programming Quiz App — 155 questions with explanations covering functions, pointers, memory, and more.
Download on Google Play →

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>