pickuma.
Dev Knowledge

Pass by Value vs Pass by Reference

Why mutating an object inside a function works in Python and Java but reassigning the parameter doesn't — and how that differs from C copies and C++ references.

5 min read

“Is this language pass by value or pass by reference?” sounds like a yes/no question, but it trips up experienced developers because the honest answer for most popular languages is “neither, exactly.” The confusion is worth clearing up once, because it explains a whole class of bugs.

The two real mechanisms

Pass by value means the function receives a copy of the argument. Whatever the function does to its parameter, the caller’s original variable is unaffected, because they are two separate pieces of memory.

Pass by reference means the function receives an alias — another name for the caller’s variable itself. Assigning to the parameter assigns to the caller’s variable. There is only one piece of memory, under two names.

C is strictly pass by value. When you pass an int, the function gets a copy:

void tryToChange(int x) { x = 99; } // local copy only
int a = 1;
tryToChange(a); // a is still 1

To let a C function change the caller’s variable, you pass a pointer — the value of an address — and dereference it. That is still pass by value (you copied the pointer), but the copied address points back at the original. C++ adds true pass by reference with the & syntax: void change(int& x) makes x a genuine alias, so x = 99 updates the caller’s variable directly.

Why Python and Java confuse everyone

Python and Java are pass by value. The catch is what the value is: for objects, the value is a reference (an object identity / handle), and that reference is copied into the parameter. So the function and the caller now hold two separate references that point at the same object.

This produces the behavior that looks like two contradictory rules:

def demo(lst):
lst.append(4) # MUTATES the shared object -> caller sees [1, 2, 3, 4]
lst = [99] # REBINDS the local name only -> caller unaffected
data = [1, 2, 3]
demo(data)
print(data) # [1, 2, 3, 4]

The append reaches through the reference and modifies the one object both names point to, so the change is visible to the caller. The assignment lst = [99] only points the local copy of the reference at a new object; the caller’s reference still points at the original list. Java behaves identically: mutate a passed object’s fields and the caller sees it; reassign the parameter and the caller does not.

This is why the precise name is pass by value of a reference, sometimes called pass by object sharing or call by sharing. It is neither classical pass by reference (because reassignment does not propagate) nor a deep copy (because mutation does propagate).

Practical consequences

This distinction is not pedantry; it predicts real behavior. A function that “clears” a list by writing lst = [] silently does nothing to the caller — you wanted lst.clear(). A function that takes a config dict and adds defaults via config["timeout"] = 30 will leak those defaults back to every caller unless you copy first. Immutable types (Python’s int, str, tuple; Java’s primitives and String) sidestep the whole issue: since you can’t mutate them, the only way to “change” one is to reassign, which never escapes the function.

The mental model to keep: assignment in these languages binds a name to an object; it never copies the object and never copies the caller’s binding. Everything else follows.

FAQ

Is Java pass by reference for objects?+
No. Java is always pass by value. For objects the value passed is a reference, and that reference is copied. You can mutate the object through the copy, but reassigning the parameter never changes which object the caller's variable refers to.
How do I actually change a caller's variable in Python?+
You cannot rebind the caller's name from inside a function. Instead, mutate a mutable object the caller holds (e.g. list.append, dict update), or return the new value and let the caller reassign it. Returning is usually the clearer choice.
Does passing a large object copy all its data?+
No. In Python and Java only the reference (a small handle) is copied, not the underlying object, so passing a huge list or array is cheap. The cost is that the function shares the same object, which is exactly why accidental mutation is a hazard.

Related reading

See all Dev Knowledge articles →

Get the best tools, weekly

One email every Friday. No spam, unsubscribe anytime.