Introduction
Parameter passing is the mechanism by which values are passed from the calling function to the called function. In C, there are different methods of parameter passing that affect how data is transferred and whether the original data can be modified. Understanding these methods is crucial for function design and memory management.
Key Concepts
Parameter: Variable in function definition that receives values Argument: Actual value passed to function when called Call by Value: Copies of arguments are passed to function Call by Reference: Addresses of arguments are passed to function Formal Parameters: Parameters in function definition Actual Parameters: Arguments passed during function call
Types of Parameter Passing
1. Call by Value (Pass by Value)
#include <stdio.h>
// Function that receives copies of arguments
void modifyValue(int x, int y) {
printf("Inside function before modification: x = %d, y = %d\n", x, y);
x = 100;
y = 200;
printf("Inside function after modification: x = %d, y = %d\n", x, y);
}
void swapByValue(int a, int b) {
printf("Inside swap before: a = %d, b = %d\n", a, b);
int temp = a;
a = b;
b = temp;
printf("Inside swap after: a = %d, b = %d\n", a, b);
}
int main() {
int num1 = 10, num2 = 20;
printf("Call by Value Demo:\n");
printf("Before function call: num1 = %d, num2 = %d\n", num1, num2);
modifyValue(num1, num2);
printf("After function call: num1 = %d, num2 = %d\n", num1, num2);
printf("\nSwap by Value Demo:\n");
printf("Before swap: num1 = %d, num2 = %d\n", num1, num2);
swapByValue(num1, num2);
printf("After swap: num1 = %d, num2 = %d\n", num1, num2);
return 0;
}
2. Call by Reference (Pass by Reference)
#include <stdio.h>
// Function that receives addresses of arguments
void modifyByReference(int *x, int *y) {
printf("Inside function before modification: *x = %d, *y = %d\n", *x, *y);
*x = 100;
*y = 200;
printf("Inside function after modification: *x = %d, *y = %d\n", *x, *y);
}
void swapByReference(int *a, int *b) {
printf("Inside swap before: *a = %d, *b = %d\n", *a, *b);
int temp = *a;
*a = *b;
*b = temp;
printf("Inside swap after: *a = %d, *b = %d\n", *a, *b);
}
int main() {
int num1 = 10, num2 = 20;
printf("Call by Reference Demo:\n");
printf("Before function call: num1 = %d, num2 = %d\n", num1, num2);
modifyByReference(&num1, &num2);
printf("After function call: num1 = %d, num2 = %d\n", num1, num2);
// Reset values
num1 = 10; num2 = 20;
printf("\nSwap by Reference Demo:\n");
printf("Before swap: num1 = %d, num2 = %d\n", num1, num2);
swapByReference(&num1, &num2);
printf("After swap: num1 = %d, num2 = %d\n", num1, num2);
return 0;
}
3. Array Parameter Passing
#include <stdio.h>
// Arrays are always passed by reference
void modifyArray(int arr[], int size) {
printf("Inside function - modifying array:\n");
for (int i = 0; i < size; i++) {
arr[i] = arr[i] * 2;
printf("arr[%d] = %d\n", i, arr[i]);
}
}
void printArray(int arr[], int size) {
printf("Array elements: ");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
// Alternative syntax for array parameters
void processArray(int *arr, int size) {
printf("Processing array using pointer notation:\n");
for (int i = 0; i < size; i++) {
*(arr + i) += 10; // Add 10 to each element
}
}
int main() {
int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]);
printf("Array Parameter Passing Demo:\n");
printf("Original array:\n");
printArray(numbers, size);
modifyArray(numbers, size);
printf("After modification:\n");
printArray(numbers, size);
processArray(numbers, size);
printf("After processing:\n");
printArray(numbers, size);
return 0;
}
Advanced Parameter Passing Concepts
1. Passing Structures
#include <stdio.h>
typedef struct {
int id;
char name[50];
float salary;
} Employee;
// Pass by value - structure is copied
void displayEmployeeByValue(Employee emp) {
printf("Employee (by value):\n");
printf("ID: %d, Name: %s, Salary: %.2f\n", emp.id, emp.name, emp.salary);
// Modifications don't affect original
emp.salary = 60000.0;
printf("Modified salary inside function: %.2f\n", emp.salary);
}
// Pass by reference - address of structure is passed
void modifyEmployeeByReference(Employee *emp) {
printf("Employee (by reference):\n");
printf("ID: %d, Name: %s, Salary: %.2f\n", emp->id, emp->name, emp->salary);
// Modifications affect original
emp->salary = 70000.0;
printf("Modified salary inside function: %.2f\n", emp->salary);
}
int main() {
Employee emp = {101, "John Doe", 50000.0};
printf("Structure Parameter Passing:\n");
printf("Original employee salary: %.2f\n", emp.salary);
displayEmployeeByValue(emp);
printf("After pass by value, salary: %.2f\n", emp.salary);
modifyEmployeeByReference(&emp);
printf("After pass by reference, salary: %.2f\n", emp.salary);
return 0;
}
2. Passing Pointers
#include <stdio.h>
#include <stdlib.h>
// Passing pointer by value
void modifyPointerValue(int *ptr) {
*ptr = 100; // Modifies the value pointed to
printf("Inside function: *ptr = %d\n", *ptr);
}
// Passing pointer by reference (pointer to pointer)
void modifyPointerReference(int **ptr) {
*ptr = malloc(sizeof(int)); // Allocates new memory
**ptr = 200; // Sets value at new location
printf("Inside function: **ptr = %d\n", **ptr);
}
void allocateMemory(int **ptr, int value) {
*ptr = malloc(sizeof(int));
if (*ptr != NULL) {
**ptr = value;
printf("Memory allocated and value set to: %d\n", **ptr);
}
}
int main() {
int x = 50;
int *ptr1 = &x;
printf("Pointer Parameter Passing:\n");
printf("Original value: %d\n", x);
modifyPointerValue(ptr1);
printf("After modifying through pointer: %d\n", x);
int *ptr2 = NULL;
modifyPointerReference(&ptr2);
printf("Value at new memory location: %d\n", *ptr2);
int *ptr3 = NULL;
allocateMemory(&ptr3, 300);
printf("Allocated value: %d\n", *ptr3);
// Clean up
free(ptr2);
free(ptr3);
return 0;
}
3. Function Pointers as Parameters
#include <stdio.h>
// Different mathematical operations
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
int subtract(int a, int b) {
return a - b;
}
// Function that takes function pointer as parameter
int calculate(int x, int y, int (*operation)(int, int)) {
return operation(x, y);
}
// Function that takes array and operation function
void processArray(int arr[], int size, int (*func)(int, int), int operand) {
for (int i = 0; i < size; i++) {
arr[i] = func(arr[i], operand);
}
}
int main() {
printf("Function Pointer Parameters:\n");
int a = 10, b = 5;
// Using function pointers as parameters
printf("Using add function: %d + %d = %d\n", a, b, calculate(a, b, add));
printf("Using multiply function: %d * %d = %d\n", a, b, calculate(a, b, multiply));
printf("Using subtract function: %d - %d = %d\n", a, b, calculate(a, b, subtract));
// Processing array with function
int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]);
printf("\nOriginal array: ");
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
processArray(numbers, size, add, 10);
printf("After adding 10 to each element: ");
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
return 0;
}
Variable Arguments (Variadic Functions)
Using stdarg.h
#include <stdio.h>
#include <stdarg.h>
// Function with variable number of arguments
int sum(int count, ...) {
va_list args;
va_start(args, count);
int total = 0;
for (int i = 0; i < count; i++) {
total += va_arg(args, int);
}
va_end(args);
return total;
}
// Variable arguments with different types
void printValues(int count, ...) {
va_list args;
va_start(args, count);
for (int i = 0; i < count; i++) {
int value = va_arg(args, int);
printf("Value %d: %d\n", i + 1, value);
}
va_end(args);
}
// Custom printf-like function
void myPrintf(const char *format, ...) {
va_list args;
va_start(args, format);
vprintf(format, args); // Use vprintf for variable arguments
va_end(args);
}
int main() {
printf("Variable Arguments Demo:\n");
// Sum with different number of arguments
printf("Sum of 2 numbers: %d\n", sum(2, 10, 20));
printf("Sum of 4 numbers: %d\n", sum(4, 1, 2, 3, 4));
printf("Sum of 6 numbers: %d\n", sum(6, 5, 10, 15, 20, 25, 30));
printf("\nPrinting values:\n");
printValues(3, 100, 200, 300);
printf("\nCustom printf:\n");
myPrintf("Hello %s, you have %d messages\n", "User", 5);
return 0;
}
Parameter Validation and Error Handling
Input Validation
#include <stdio.h>
#include <stdlib.h>
// Function with parameter validation
int safeDivide(int dividend, int divisor, int *result) {
if (result == NULL) {
printf("Error: Result pointer is NULL\n");
return -1;
}
if (divisor == 0) {
printf("Error: Division by zero\n");
return -2;
}
*result = dividend / divisor;
return 0; // Success
}
// Array bounds checking
void safeArrayAccess(int arr[], int size, int index, int *value) {
if (arr == NULL) {
printf("Error: Array pointer is NULL\n");
return;
}
if (index < 0 || index >= size) {
printf("Error: Index %d out of bounds (0-%d)\n", index, size - 1);
return;
}
if (value != NULL) {
*value = arr[index];
}
}
// String parameter validation
int stringLength(const char *str) {
if (str == NULL) {
printf("Error: String pointer is NULL\n");
return -1;
}
int length = 0;
while (str[length] != '\0') {
length++;
}
return length;
}
int main() {
printf("Parameter Validation Demo:\n");
// Safe division
int result;
int status = safeDivide(20, 4, &result);
if (status == 0) {
printf("Division result: %d\n", result);
}
status = safeDivide(20, 0, &result); // Division by zero
// Safe array access
int numbers[] = {10, 20, 30, 40, 50};
int size = sizeof(numbers) / sizeof(numbers[0]);
int value;
safeArrayAccess(numbers, size, 2, &value);
printf("Array element at index 2: %d\n", value);
safeArrayAccess(numbers, size, 10, &value); // Out of bounds
// String validation
char *validString = "Hello World";
char *nullString = NULL;
printf("Length of valid string: %d\n", stringLength(validString));
stringLength(nullString); // NULL pointer
return 0;
}
Best Practices
- Use call by reference when you need to modify original values
- Use call by value for read-only operations or small data
- Validate parameters before using them in functions
- Use const for read-only parameters to prevent accidental modification
- Document parameter requirements clearly in comments
- Consider memory efficiency when passing large structures
- Handle NULL pointers gracefully
- Use meaningful parameter names for better code readability
Performance Considerations
Memory Usage Comparison
#include <stdio.h>
#include <string.h>
typedef struct {
char data[1000]; // Large structure
int id;
} LargeStruct;
// Pass by value - copies entire structure
void processByValue(LargeStruct ls) {
ls.id = 999;
printf("Processing by value: ID = %d\n", ls.id);
}
// Pass by reference - only copies address
void processByReference(LargeStruct *ls) {
ls->id = 888;
printf("Processing by reference: ID = %d\n", ls->id);
}
// Read-only access using const
void displayStruct(const LargeStruct *ls) {
printf("Display struct: ID = %d\n", ls->id);
// ls->id = 777; // Error: cannot modify const parameter
}
int main() {
LargeStruct myStruct;
strcpy(myStruct.data, "Large amount of data");
myStruct.id = 100;
printf("Performance Considerations:\n");
printf("Original ID: %d\n", myStruct.id);
processByValue(myStruct); // Expensive copy
printf("After pass by value: %d\n", myStruct.id);
processByReference(&myStruct); // Efficient
printf("After pass by reference: %d\n", myStruct.id);
displayStruct(&myStruct); // Efficient read-only
return 0;
}
Summary
Parameter passing in C includes call by value (copies arguments) and call by reference (passes addresses). Arrays and structures can be passed efficiently using pointers. Function pointers enable flexible parameter passing for callbacks. Variable arguments allow functions to accept different numbers of parameters. Proper parameter validation and choosing the right passing method based on data size and modification requirements are essential for efficient programming.
Part of BCA Programming with C Course (UGCOA22J201)