LanguagesCore FundamentalsPass-by-Value vs. Pass-by-Reference vs. Pass-by-Sharing

Pass-by-Value vs. Pass-by-Reference vs. Pass-by-Sharing

Core Fundamentals

How is Data Passed to Functions?

When you pass an argument to a function, the programming language needs a mechanism to get that data into the function's scope. This mechanism is called the parameter passing strategy. Understanding this is critical because it determines whether a function can modify the original data you pass to it.

This is one of the most commonly misunderstood topics in programming, and clarifying it in an interview shows a deep level of understanding. The three main models are:

  1. Pass-by-Value
  2. Pass-by-Reference
  3. Pass-by-Sharing (The model used by Python, Java, JavaScript, Ruby, and others)

1. Pass-by-Value

In a pass-by-value system, when a function is called, it receives a copy of the argument's value. The function's parameter is a new variable in memory, initialized with the value of the argument.

Key Implication: The function cannot change the original variable outside its scope. Any modifications are made to the local copy only.

  • Languages: C, C++, C#, Go, Swift (for value types like structs and enums).
#include <iostream>

// The 'num' parameter is a copy of the 'original_number' argument.
void increment(int num) {
    num = num + 1;
    std::cout << "Value inside function: " << num << std::endl; // Prints 11
}

int main() {
    int original_number = 10;
    increment(original_number);
    std::cout << "Value outside function: " << original_number << std::endl; // Prints 10
    return 0;
}

Explanation: The increment function receives a copy of original_number. When it modifies num, it's only changing its local copy. The original_number remains untouched.


2. Pass-by-Reference

In a pass-by-reference system, the function receives a reference (or an alias) to the original variable. It does not get a copy of the value; it gets the memory address of the original data.

Key Implication: The function can modify the original variable because the parameter is just another name for the same piece of data in memory.

  • Languages: C++ (using &), Swift (using inout), Pascal.
#include <iostream>

// The '&' makes 'num' a reference to the original variable.
void increment(int &num) {
    num = num + 1;
    std::cout << "Value inside function: " << num << std::endl; // Prints 11
}

int main() {
    int original_number = 10;
    increment(original_number);
    std::cout << "Value outside function: " << original_number << std::endl; // Prints 11
    return 0;
}

Explanation: The increment function receives a direct link to original_number. When it modifies num, it is directly modifying original_number in the main function's scope.


3. Pass-by-Sharing (The "Python/Java Model")

This is the model that causes the most confusion. It's often incorrectly called "pass-by-reference." In pass-by-sharing (also known as pass-by-object-reference or pass-by-assignment), the function receives a copy of the reference to the object.

Let's break that down:

  • The variable itself is not an object; it's a reference (a pointer) to an object in memory.
  • When you pass this variable to a function, the function gets a copy of that reference.
  • Both the original reference (outside the function) and the copied reference (inside the function) point to the exact same object in memory.

Key Implications:

  1. You can mutate the object: If the object is mutable (like a list in Python or an object in JavaScript), the function can change the object's internal state, and the changes will be visible outside the function.
  2. You cannot reassign the original variable: If you try to reassign the parameter to a completely new object inside the function, it only affects the function's local copy of the reference. The original variable outside the function remains unchanged.
  • Languages: Python, Java, JavaScript, Ruby, C# (for reference types like classes).

Example: Mutating the Object (Visible Change)

# Python: Lists are mutable
def add_item(my_list):
    # 'my_list' is a copy of the reference, but it points to the same list object.
    my_list.append(4) # We are MUTATING the original object.

data = [1, 2, 3]
add_item(data)
print(data) # Output: [1, 2, 3, 4]

Example: Reassigning the Reference (No Visible Change)

# Python
def reassign_list(my_list):
    # This creates a NEW list and makes the local 'my_list' reference point to it.
    # The original 'data' variable is unaffected.
    my_list = [10, 20, 30]

data = [1, 2, 3]
reassign_list(data)
print(data) # Output: [1, 2, 3]

Summary for Interviews

  • Pass-by-Value: Function gets a copy of the value. Cannot change the original. (C, C++)
  • Pass-by-Reference: Function gets a reference to the original variable. Can change the original. (C++ with &)
  • Pass-by-Sharing: Function gets a copy of the reference.
    • Can change the internal state of a mutable object.
    • Cannot reassign the original variable to a new object.
    • This is how most modern, high-level languages work (Python, Java, JS, Ruby).

When asked "Is Python pass-by-value or pass-by-reference?", the most accurate answer is: "It's neither, it's pass-by-sharing. This means that for mutable types like lists, it can feel like pass-by-reference because you can modify the object's contents. However, you can't reassign the original variable, which shows it's not true pass-by-reference."