Parameter passing mechanisms determine how arguments are passed when calling functions in programming languages. This core concept has been studied since the early programming language design era of the 1960s and continues to directly impact code behavior and performance in modern programming. Call by Value and Call by Reference are the two most fundamental passing methods. Understanding the advantages and disadvantages of each method enables writing more efficient and safer code.

Overview of Function Parameter Passing

What is Parameter Passing Mechanism?

A parameter passing mechanism defines how actual parameter values or references are passed to formal parameters during function calls. Various methods exist including Call by Value, Call by Reference, Call by Name, and Call by Need.

Historical Background

The concept of parameter passing mechanisms evolved alongside programming language design in the 1960s. ALGOL 60 was one of the first languages to support both Call by Value and Call by Name. Most subsequent programming languages designed their parameter passing mechanisms based on these concepts.

YearLanguage/ConceptParameter Passing Method
1960ALGOL 60Call by Value, Call by Name
1972CCall by Value (reference simulated via pointers)
1979C++Call by Value, Call by Reference
1995JavaCall by Value (object reference value passing)
1991PythonCall by Object Reference (Call by Sharing)

Comparison of Major Passing Methods

CharacteristicCall by ValueCall by Reference
What is passedCopy of the valueAddress/reference of original
Original modificationNot possiblePossible
Memory usageAdditional memory needed for copyEfficient, only address passed
PerformanceSlower for large dataFaster for large data
SafetyOriginal protectedOriginal can be modified
Use casesPrimitive types, when original protection neededLarge objects, multiple return values

Call by Value

What is Call by Value?

Call by Value copies the actual parameter’s value and passes it to the formal parameter during function calls. Even if the parameter value is changed inside the function, it does not affect the caller’s original variable. This is the most basic and safe parameter passing method.

Memory Operation Principle

In Call by Value, the actual parameter’s value is copied to stack memory during the function call, creating a new local variable. This copy is only valid within the function’s scope and is removed from the stack when the function terminates.

Memory state before call:
┌─────────────┐
│ main()      │
│ a = 10      │  ← Original variable
│ b = 20      │
└─────────────┘

After swap(a, b) call:
┌─────────────┐
│ swap()      │
│ a = 10      │  ← Copied value (separate memory)
│ b = 20      │  ← Copied value (separate memory)
├─────────────┤
│ main()      │
│ a = 10      │  ← Original (unchanged)
│ b = 20      │
└─────────────┘

Call by Value in C

C fundamentally supports only Call by Value. All function arguments are passed by value. When pass-by-reference is needed, pointers are used to simulate it.

#include <stdio.h>

void swap_by_value(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    printf("Inside function: a = %d, b = %d\n", a, b);
}

int main() {
    int x = 10, y = 20;
    printf("Before call: x = %d, y = %d\n", x, y);
    swap_by_value(x, y);
    printf("After call: x = %d, y = %d\n", x, y);
    return 0;
}

Output:

Before call: x = 10, y = 20
Inside function: a = 20, b = 10
After call: x = 10, y = 20

Although a and b values were swapped inside the function, x and y in the main function remained unchanged. This is because the values of x and y were copied when passed.

Advantages and Disadvantages of Call by Value

AdvantagesDisadvantages
Original data protectionPerformance degradation when copying large data
Prevents side effectsIncreased memory usage
Improved code predictabilityDifficulty handling multiple return values
Easier debuggingRequires additional handling when original modification needed

Call by Reference

What is Call by Reference?

Call by Reference passes the memory address (reference) of the actual parameter to the formal parameter during function calls. The function can directly access and modify the original variable through the parameter. This method is memory efficient but carries the risk of original modification.

Memory Operation Principle

In Call by Reference, the memory address of the actual parameter is passed during the function call. The formal parameter acts as an alias pointing to the original variable, referencing the same memory location.

Memory state before call:
┌─────────────┐
│ main()      │
│ a = 10      │  Address: 0x1000
│ b = 20      │  Address: 0x1004
└─────────────┘

After swap(&a, &b) call:
┌─────────────┐
│ swap()      │
│ a → 0x1000  │  ← Points to original a
│ b → 0x1004  │  ← Points to original b
├─────────────┤
│ main()      │
│ a = 10      │  ← Same memory location
│ b = 20      │
└─────────────┘

Simulating Reference Passing with Pointers in C

C does not support pure Call by Reference, but reference passing can be simulated using pointers. This approach is also called “Call by Address” or “Simulated Call by Reference.”

#include <stdio.h>

void swap_by_pointer(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
    printf("Inside function: *a = %d, *b = %d\n", *a, *b);
}

int main() {
    int x = 10, y = 20;
    printf("Before call: x = %d, y = %d\n", x, y);
    swap_by_pointer(&x, &y);
    printf("After call: x = %d, y = %d\n", x, y);
    return 0;
}

Output:

Before call: x = 10, y = 20
Inside function: *a = 20, *b = 10
After call: x = 20, y = 10

Since the address of the original variables was passed through pointers, changes made inside the function were reflected in x and y of the main function.

C++ References

C++ introduced references (&) to support pure Call by Reference at the language level. References are syntactically more concise than pointers and can prevent null pointer issues.

#include <iostream>
using namespace std;

void swap_by_reference(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
    cout << "Inside function: a = " << a << ", b = " << b << endl;
}

int main() {
    int x = 10, y = 20;
    cout << "Before call: x = " << x << ", y = " << y << endl;
    swap_by_reference(x, y);
    cout << "After call: x = " << x << ", y = " << y << endl;
    return 0;
}

Advantages and Disadvantages of Call by Reference

AdvantagesDisadvantages
Efficient for large data transferRisk of original data modification
Enables multiple return valuesPotential side effects
Memory savingsDifficulty tracking code
Fast performancePossibility of unintended modifications

Language-Specific Parameter Passing Methods

Java: Call by Value (Object Reference Value Passing)

Everything in Java is passed by Call by Value. Primitive types have their values copied, while object types have their reference values copied and passed.

public class ParameterPassing {
    public static void modifyPrimitive(int value) {
        value = 100;
        System.out.println("Inside function value: " + value);
    }

    public static void modifyObject(StringBuilder sb) {
        sb.append(" World");
        System.out.println("Inside function sb: " + sb);
    }

    public static void reassignObject(StringBuilder sb) {
        sb = new StringBuilder("New Object");
        System.out.println("Inside function sb: " + sb);
    }

    public static void main(String[] args) {
        int num = 10;
        modifyPrimitive(num);
        System.out.println("After call num: " + num);

        StringBuilder str = new StringBuilder("Hello");
        modifyObject(str);
        System.out.println("After call str: " + str);

        StringBuilder str2 = new StringBuilder("Original");
        reassignObject(str2);
        System.out.println("After call str2: " + str2);
    }
}

Output:

Inside function value: 100
After call num: 10
Inside function sb: Hello World
After call str: Hello World
Inside function sb: New Object
After call str2: Original

In Java, object contents can be modified, but reassigning the reference itself does not affect the original. This is because the “value” of the reference is copied when passed.

Python: Call by Object Reference (Call by Sharing)

Python uses a method called Call by Object Reference or Call by Sharing. Everything is an object and object references are passed, but mutable and immutable objects behave differently.

def modify_list(lst):
    lst.append(4)
    print(f"Inside function list: {lst}")

def modify_number(num):
    num = 100
    print(f"Inside function number: {num}")

def reassign_list(lst):
    lst = [10, 20, 30]
    print(f"After reassignment inside function: {lst}")

# Mutable object (list)
my_list = [1, 2, 3]
modify_list(my_list)
print(f"After call list: {my_list}")

# Immutable object (integer)
my_num = 10
modify_number(my_num)
print(f"After call number: {my_num}")

# Reassignment
my_list2 = [1, 2, 3]
reassign_list(my_list2)
print(f"After call list2: {my_list2}")

Output:

Inside function list: [1, 2, 3, 4]
After call list: [1, 2, 3, 4]
Inside function number: 100
After call number: 10
After reassignment inside function: [10, 20, 30]
After call list2: [1, 2, 3]

Language Comparison Summary

LanguagePrimitive TypesObject/Reference TypesCharacteristics
CCall by ValueSimulated via pointersPure Call by Value
C++Call by ValueChoice of Value/ReferenceReference (&) support
JavaCall by ValueCall by Value of referenceNo pure Call by Reference
Python-Call by Object ReferenceEverything is an object
JavaScriptCall by ValueCall by SharingSimilar to Python
GoCall by ValueSimulated via pointersSimilar to C

Performance Comparison and Optimization

Performance Differences by Data Size

When passing large data structures, the performance difference between Call by Value and Call by Reference is significant. Reference passing is particularly efficient for data with high copy costs.

#include <iostream>
#include <chrono>
#include <vector>
using namespace std;

struct LargeData {
    int data[10000];
};

void process_by_value(LargeData data) {
    data.data[0] = 1;
}

void process_by_reference(LargeData &data) {
    data.data[0] = 1;
}

int main() {
    LargeData obj;
    const int iterations = 100000;

    auto start = chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; i++) {
        process_by_value(obj);
    }
    auto end = chrono::high_resolution_clock::now();
    cout << "Call by Value: "
         << chrono::duration_cast<chrono::milliseconds>(end - start).count()
         << "ms" << endl;

    start = chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; i++) {
        process_by_reference(obj);
    }
    end = chrono::high_resolution_clock::now();
    cout << "Call by Reference: "
         << chrono::duration_cast<chrono::milliseconds>(end - start).count()
         << "ms" << endl;

    return 0;
}

Safe Optimization with const References

In C++, const references maintain the performance benefits of reference passing while preventing original modification. This is the recommended approach when passing large objects as read-only.

void process_safely(const LargeData &data) {
    // data.data[0] = 1;  // Compile error: cannot modify const object
    int value = data.data[0];  // Reading is allowed
}
ScenarioRecommended MethodReason
Primitive types (int, float)Call by ValueLow copy cost
Reading large structuresconst ReferencePrevents copy, no modification
Modifying large structuresReference/PointerDirect modification needed
Multiple return valuesReference/PointerReturn multiple values
Functional programmingCall by ValueGuarantees immutability

Practical Usage Guide

Swap Function Implementation Comparison

Examples of swap function implementations in various languages and methods demonstrate the differences in parameter passing.

C (using pointers):

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

C++ (using references):

void swap(int &a, int &b) {
    int temp = a;
    a = b;
    b = temp;
}

Python (tuple unpacking):

def swap(a, b):
    return b, a

x, y = 10, 20
x, y = swap(x, y)

Array/Collection Handling

When passing arrays or collections to functions, most languages pass references, so the original can be modified. Care must be taken.

# Passing a list copy in Python
def modify_copy(lst):
    lst = lst.copy()  # Create a copy
    lst.append(4)
    return lst

original = [1, 2, 3]
modified = modify_copy(original)
print(f"Original: {original}")  # [1, 2, 3]
print(f"Modified: {modified}")  # [1, 2, 3, 4]

Callback Functions and Closures

When modifying external variables in callback functions, both parameter passing methods and closure characteristics must be considered.

function createCounter() {
    let count = 0;
    return {
        increment: function() { count++; },
        getCount: function() { return count; }
    };
}

const counter = createCounter();
counter.increment();
console.log(counter.getCount());  // 1

Conclusion

Parameter passing mechanisms are fundamental to programming yet require accurate understanding due to subtle differences across languages. Call by Value is a safe method that copies values to protect originals and prevent side effects. Call by Reference passes memory addresses to enable efficient data processing and original modification.

C simulates reference passing through pointers. C++ provides language-level support through references. Java and Python use a Call by Sharing approach that passes the value of object references. Understanding the characteristics of each method and applying them appropriately enables writing more efficient and safer code.

When handling large data, consider optimization using const references. When multiple return values are needed, reference passing or tuple returns are effective.