Passing Arguments to the Function

When you pass arguments to functions, there are several ways to do so, depending on how you want the function to interact with the arguments. The method you choose affects how the function accesses or modifies the arguments and how memory is managed. The main methods for passing arguments to functions are:

  1. Pass by Value
  2. Pass by Reference
  3. Pass by Pointer
  4. Pass by Constant Reference

1️⃣ Pass by Value

When passing by value, the function receives a copy of the argument. This means the function can work with the argument, but it won’t affect the original value in the calling function.

Example:
void increment(int num) {
    num++;  // This changes only the local copy of num
}

int main() {
    int x = 10;
    increment(x);  // x is passed by value
    // x is still 10 because the original value is not modified
}
  • Effect: The function works with a copy of x. The original x remains unchanged.
  • Use Case: When you don’t need to modify the original argument and just need a local copy to work with.
  • Overhead: For small data types like int, char, etc., passing by value is efficient, but for larger objects like complex data structures, it might be costly in terms of performance because of the copying process.
+----------------+   +----------------+
| Original Value |   | Function's Copy|
|     x = 10     |   |     x = 10     |
+----------------+   +----------------+
Passing User-Defined Type:
#include <iostream>

struct Point {
    int x;
    int y;
};

void modifyPoint(Point p) {
    p.x = 20;  // This modifies only the local copy of 'p'
    p.y = 30;
    std::cout << "Inside function (Pass by Value): (" << p.x << ", " << p.y << ")" << std::endl;
}

int main() {
    Point p1 = {10, 15};
    
    modifyPoint(p1);  // p1 is passed by value, so original p1 is not changed
    std::cout << "After function call: (" << p1.x << ", " << p1.y << ")" << std::endl;

    return 0;
}
 Output:
Inside function (Pass by Value): (20, 30)
After function call: (10, 15)

2️⃣ Pass by Reference

When passing by reference, the function gets an alias (or reference) to the original variable. Any changes made to the reference inside the function will directly affect the original variable.

Example:

void increment(int &num) {
    num++;  // This changes the original num
}

int main() {
    int x = 10;
    increment(x);  // x is passed by reference
    // x is now 11 because the original value is modified
}
  • Effect: The function operates directly on x, modifying the original value.
  • Use Case: When you want the function to modify the original variable.
  • Overhead: More efficient than pass by value for larger objects, since no copying is done, only a reference to the original object is passed.
+----------------+  
| Original Value |  
|     x = 10     |  
+----------------+
   ^
   |
 Function's Reference to x
Passing User-Defined Type:
#include <iostream>

struct Point {
    int x;
    int y;
};

void modifyPoint(Point &p) {
    p.x = 20;  // Modifies the original p1
    p.y = 30;
    std::cout << "Inside function (Pass by Reference): (" << p.x << ", " << p.y << ")" << std::endl;
}

int main() {
    Point p1 = {10, 15};
    
    modifyPoint(p1);  // p1 is passed by reference, so original p1 is modified
    std::cout << "After function call: (" << p1.x << ", " << p1.y << ")" << std::endl;

    return 0;
}
Output:
Inside function (Pass by Reference): (20, 30)
After function call: (20, 30)

3️⃣ Pass by Pointer

Passing by pointer is similar to passing by reference, but instead of passing an alias, you pass the address of the variable. Inside the function, you dereference the pointer to access or modify the variable.

Example:
void increment(int *num) {
    (*num)++;  // Dereference the pointer and increment the original num
}

int main() {
    int x = 10;
    increment(&x);  // Pass the address of x
    // x is now 11 because the original value is modified
}
  • Effect: The function operates on the variable through its pointer, allowing direct modification of the original value.
  • Use Case: Useful when working with dynamic memory or when you need to pass nullptr to indicate the absence of a value.
  • Overhead: No copying of the object, but you must deal with pointers, which adds complexity.
  • Safety: Requires careful handling since working with pointers can lead to bugs if not used correctly, such as dereferencing null or invalid pointers.
+----------------+
| Original Value |
|     x = 10     |
+----------------+
      ^
      |
Function has x's Address (&x)
Passing User-Defined Type:
#include <iostream>

struct Point {
    int x;
    int y;
};

void modifyPoint(Point *p) {
    p->x = 20;  // Dereference and modify the original p1
    p->y = 30;
    std::cout << "Inside function (Pass by Pointer): (" << p->x << ", " << p->y << ")" << std::endl;
}

int main() {
    Point p1 = {10, 15};
    
    modifyPoint(&p1);  // Pass the address of p1
    std::cout << "After function call: (" << p1.x << ", " << p1.y << ")" << std::endl;

    return 0;
}
Output:
Inside function (Pass by Pointer): (20, 30)
After function call: (20, 30)

4️⃣ Pass by Constant Reference

Passing by constant reference is used when you want to avoid copying the argument but still prevent the function from modifying the original argument. This is especially useful for large objects where copying would be inefficient, but you don't want the function to alter the data.

Example:
void print(const int &num) {
    // num cannot be modified because it's a constant reference
    std::cout << num << std::endl;
}

int main() {
    int x = 10;
    print(x);  // x is passed by constant reference
    // x remains unchanged
}
  • Effect: The function can access the original value but cannot modify it.
  • Use Case: Best for read-only access to large objects where copying is costly but you don’t want the function to alter the original value.
  • Overhead: Similar to pass by reference, but safer since the original data cannot be modified.
+----------------+  
| Original Value |  
|     x = 10     |  
+----------------+
   ^
   |
 Function's Read-only Reference to x

Comparison of the Methods:

MethodModifies Original?Copies Data?Use Case
Pass by ValueNoYesSmall data types, no modification needed
Pass by ReferenceYesNoModifying the original data
Pass by PointerYesNoDynamic memory, handling optional values
Pass by Const ReferenceNoNoLarge objects, read-only access