Call by Value vs Call by Reference in C π
Mentor's Note: Think of passing arguments to functions like sharing documents. Sometimes you hand someone a photocopy of your notesβthey can scribble on it, but your original notes remain clean. Other times, you give them the key to your locker where the original document is storedβany changes they make will affect your original document. Understanding this distinction is the key to mastering memory management in C! π‘
π Educational Content: This topic is highly relevant for GSEB Std 11/12 (Theory & Practical), CBSE Class 11/12 Computer Science, and university BCA Sem 1 examinations. Writing the "Swap Function" is a classic board exam question that examiners use to test your pointer concepts.
By the end of this tutorial, you'll know:
- The fundamental difference between Call by Value and Call by Reference.
- How parameters are allocated memory on the stack.
- Why the standard swap function fails in Call by Value.
- How to implement a correct swap function using pointers in C.
- How to select the right parameter-passing method for your program's needs.
π The Scenario: The Photocopy Shop vs. The Shared Lockerβ
Imagine you have written a story on a sheet of paper. Your friend wants to edit it. You have two choices:
- The Photocopy Shop (Call by Value): You run to a photocopy machine, make a duplicate copy of your sheet, and hand the copy to your friend. Your friend takes a red pen, strikes out lines, and writes corrections. When they are done, does your original sheet of paper have any red ink? Absolutely not! The changes were restricted to the copy.
- The Shared Locker (Call by Reference): You put your original sheet of paper inside a school locker, lock it, and give your friend a duplicate key (or the locker address). Your friend opens the locker, takes out the original sheet, writes corrections directly on it, and puts it back. When you open the locker later, your original sheet has changed!
In C programming:
- The sheet of paper is a Variable.
- The photocopy represents Call by Value.
- The locker address represents Call by Reference.
π Concept Explanationβ
In C, variables passed into functions are called arguments (or actual parameters), and the variables receiving them in the function definition are called parameters (or formal parameters). C uses two ways to pass these parameters.
1. Call by Valueβ
In Call by Value, the value of the actual parameter is copied into the formal parameter of the function.
- Memory Allocation: The function's parameters are allocated separate memory locations on the call stack.
- Scope Isolation: Any change made to the formal parameters inside the function has zero effect on the actual parameters in
main(). - Default Behavior: This is the default parameter-passing mechanism for primitive types (like
int,float,char) in C.
2. Call by Reference (via Pointers)β
C is strictly a Call by Value language under the hood, but it simulates Call by Reference by passing the memory address of a variable instead of its value.
- Pointers as Parameters: The function's formal parameters are declared as pointer variables (e.g.,
int *x). - Direct Modification: When you call the function, you pass the variable's address using the address-of operator (
&a). Inside the function, you dereference the pointer using the asterisk operator (*x) to read or write directly to the caller's memory block.
Where is it used?β
- Web Development / Systems: Passing database connection structs by reference to avoid duplicating heavy objects.
- Enterprise Software: Passing configuration settings by value to prevent utility functions from accidentally modifying system-wide constants.
π§ Algorithm & Step-by-Step Logicβ
Let's design a program to swap (exchange) the values of two variables:
Call by Value Swap (Incorrect Logic)β
- Start π
- Send values of
aandbtoswap_by_value(num1, num2). - In
swap_by_value, copynum1totemp. - Copy
num2tonum1. - Copy
temptonum2. (Formal parameters are swapped). - End (Notice that
aandbinmainremain unchanged). π
Call by Reference Swap (Correct Logic)β
- Start π
- Send addresses of
aandb(&aand&b) toswap_by_reference(num1, num2). - In
swap_by_reference, dereferencenum1and store the value intemp. - Copy value at address
num2to the address pointed to bynum1. - Copy
tempto the address pointed to bynum2. (Memory cells ofaandbare directly swapped). - End (Values of
aandbinmainare successfully swapped). π
π» Implementationsβ
Let's write both implementations in C to see the difference.
1. Call by Value: The Incorrect Swapβ
#include <stdio.h>
// Incorrect swap function using Call by Value
void swap_by_value(int num1, int num2) {
int temp = num1;
num1 = num2;
num2 = temp;
printf("Inside function: num1 = %d, num2 = %d\n", num1, num2);
}
int main() {
int a = 10;
int b = 20;
printf("Before swap: a = %d, b = %d\n", a, b);
// Passing values of a and b
swap_by_value(a, b);
printf("After swap: a = %d, b = %d\n", a, b);
return 0;
}
// Output:
// Before swap: a = 10, b = 20
// Inside function: num1 = 20, num2 = 10
// After swap: a = 10, b = 20
Explanation:
When swap_by_value(a, b) is called, the value of a (10) is copied into num1, and b (20) is copied into num2. Inside the function, the copies swap places. However, once the function finishes execution, its stack frame is destroyed. When control returns to main(), a and b still have their original values.
2. Call by Reference: The Correct Swapβ
#include <stdio.h>
// Correct swap function using pointers (Call by Reference)
void swap_by_reference(int *num1, int *num2) {
int temp = *num1; // Store value at address num1 in temp
*num1 = *num2; // Assign value at address num2 to address num1
*num2 = temp; // Assign temp value to address num2
printf("Inside function: *num1 = %d, *num2 = %d\n", *num1, *num2);
}
int main() {
int a = 10;
int b = 20;
printf("Before swap: a = %d, b = %d\n", a, b);
// Passing addresses of a and b using the address-of operator (&)
swap_by_reference(&a, &b);
printf("After swap: a = %d, b = %d\n", a, b);
return 0;
}
// Output:
// Before swap: a = 10, b = 20
// Inside function: *num1 = 20, *num2 = 10
// After swap: a = 20, b = 10
Explanation:
We pass &a and &b to swap_by_reference. The variables num1 and num2 are declared as pointers (int *), storing memory addresses. By using the dereferencing operator (*), we reach out of the function's local boundary and directly swap the values inside the memory cells of a and b.
π Sample Dry Runβ
Let's trace how the variables change during swap_by_reference(&a, &b). Assume variable a is stored at memory address 0x100 and b is stored at 0x104.
| Step | Instruction | a Value (at 0x100) | b Value (at 0x104) | num1 Value | num2 Value | temp Value | Description |
|---|---|---|---|---|---|---|---|
| 1 | int a = 10, b = 20; | 10 | 20 | - | - | - | Initial variables declared in main. |
| 2 | swap_by_reference(&a, &b); | 10 | 20 | 0x100 | 0x104 | - | Addresses are passed. Pointers point to variables. |
| 3 | int temp = *num1; | 10 | 20 | 0x100 | 0x104 | 10 | temp stores the value at address 0x100 (a). |
| 4 | *num1 = *num2; | 20 | 20 | 0x100 | 0x104 | 10 | Value at 0x104 (b) is written to address 0x100 (a). |
| 5 | *num2 = temp; | 20 | 10 | 0x100 | 0x104 | 10 | temp value (10) is written to address 0x104 (b). |
| 6 | Function Ends | 20 | 10 | - | - | - | Stack frame destroyed. Swapping successful in main. |
π Complexity Analysisβ
Time Complexity β±οΈβ
- Best, Average, and Worst Case: $O(1)$ - The parameter-passing and swapping logic require a constant number of memory accesses, making it run instantly regardless of input values.
Space Complexity πΎβ
- Auxiliary Space: $O(1)$ - Only one extra temporary integer variable (
temp) is created in memory. - Total Space: $O(1)$ - Memory does not grow dynamically.
π¨ Visual Logic & Diagramsβ
π Best Practices & Common Mistakesβ
β Best Practicesβ
- Use
constpointers when read-only is needed: If you pass a large struct by reference for speed but do not want it modified, declare it asconst my_struct *ptr. - Check for
NULLpointers: Before dereferencing pointers in a function, check if they areNULLto avoid segmentation faults.if (num1 == NULL || num2 == NULL) return;
β Common Mistakes β οΈβ
- Passing Value instead of Address: Writing
swap(a, b)when the function expectsswap(&a, &b). - Forgetting to Dereference: Writing
num1 = num2;instead of*num1 = *num2;inside the swap function. This swaps the addresses stored inside the pointer variables, not the actual values they point to! - Returning Local Address: Never return the address of a local variable from a function. Since local variables are destroyed when the function exits, returning their address results in a "dangling pointer" pointing to garbage memory.
π― Practice Problemsβ
Easy Level π’β
- Write a function
double_value(int *x)that doubles the value of an integer variable using Call by Reference. - Design a program that accepts an integer in
main()and increments it by 5 using Call by Reference.
Medium Level π‘β
- Write a function
find_stats(int arr[], int size, int *min, int *max)that finds the minimum and maximum values in an array and returns both values tomainusing Call by Reference. - Implement a custom division function
divide(int a, int b, int *quotient, int *remainder)that returns both results.
Hard Level π΄β
- Write a function
circular_shift(int *a, int *b, int *c)that rotates the values:agetsb's value,bgetsc's value, andcgetsa's value. Try to implement it without using more than one temporary variable.
β Frequently Asked Questionsβ
Q: Does C natively support Call by Reference?
No. C does not have native reference parameters (like C++ does with the & symbol in function signatures, e.g., void swap(int &a, int &b)). C strictly uses Call by Value. We achieve Call by Reference in C by passing pointer values (addresses) by value. Since the address is copied, we can dereference it to modify the original value.
Q: When should I choose Call by Value over Call by Reference?
Use Call by Value for small variables (like int, float, char) when you do not need to modify the original variable. This protects your original variable from being accidentally changed by the function. Use Call by Reference when you need to modify the original variable, return multiple values, or pass heavy data structures like structs (to save memory by not copying the structure).
Q: Why do C arrays always behave like Call by Reference?
In C, when you pass an array name as an argument to a function, it automatically "decays" into a pointer to its first element. Therefore, arrays are always passed by address, and any modifications to the array elements inside the function will change the original array.
β Summaryβ
In this tutorial, you've learned:
- β Call by Value duplicates variable values, making modifications safe but isolated.
- β Call by Reference copies memory addresses using pointers, allowing direct changes to the caller's variables.
- β
Swapping variables requires passing addresses (
&a,&b) to function pointer parameters. - β Memory efficiency is higher with Call by Reference for larger objects because only an address (4 or 8 bytes) is passed.
- β C is strictly Call by Value but simulates Call by Reference using pointers.
π‘ Interview Tips & Board Focus πβ
- Board Exam Alert: You will often be asked: "Distinguish between Call by Value and Call by Reference in C with an example." Always write the definitions and then illustrate with both swap functions shown above.
- Viva Voce Question: "What is the size of a pointer parameter passed in Call by Reference?" A pointer variable's size depends on the system architecture (4 bytes on 32-bit, 8 bytes on 64-bit), regardless of the data type it points to.
- Key Terms to Mention: Formal parameters, actual parameters, stack frame allocation, dereferencing, address-of operator.
π Further Readingβ
Continue your learning path:
Go deeper: