Passing Function Arguments in C Programming

A function sometime needs data from calling function to perform it's task. It accepts data from calling function by declare variables that accept the values of the calling functions. These variables are called the formal parameters of the function.

In C, parameter(arguments) refers to data which is passed to function while calling function. The formal parameters are similar to local variables inside the function scope and are created when control enters into the function and gets destroyed upon exit.

A Function in C can be called in two ways.

  • Call by value

  • Call by reference

First of all we should understand the meaning of actual and formal parameters

  • Formal parameter
    This is the argument which is used inside body of function definition. It's is limited to the scope of function. It gets created when control enters function and gets destroyed when control exits from function.
  • Actual parameter
    This is the argument which is used while calling a function.

Call by value

In call by value method a copy of actual arguments is passed into formal arguments in the function definition. Any change in the formal parameters of the function have no effect on the value of actual argument. Call by value is the default method of passing parameters in C. Different memories are allocated for the formal and actual parameters.

C Program to Pass Arguments as Call by Value
#include<stdio.h>
 
void getDoubleValue(int F){
   F = F*2;
   printf("F(Formal Parameter) = %d\n", F);
}

int main(){
   int A;
   printf("Enter a numbers\n");
   scanf("%d", &A);
   /* Calling function using call by value */
   getDoubleValue(A);
   /* Any change in the value of formal parameter(F)
   will not effect the value of actual parameter(A) */
   printf("A(Actual Parameter) = %d\n", A);
   
   return 0;
}
Above program find sum of all integers from 1 to N, using while loop.

Output
Enter a numbers
5
F(Formal Parameter) = 10
A(Actual Parameter) = 5

Above program shows that, their is no change in the value of A(Actual parameter) even if the value of F(Formal parameter) changed inside function.


Call by reference

In call by reference method the address of the variable is passed to the formal arguments of a function. Any change in the formal parameters of the function will effect the value of actual argument. Same memory location is accessed by the formal and actual parameters.

C Program to Pass Arguments as Call by Reference
#include<stdio.h>
 
void getDoubleValue(int *F){
   *F = *F + 2;
   printf("F(Formal Parameter) = %d\n", *F);
}

int main(){
   int A;
   printf("Enter a numbers\n");
   scanf("%d", &A);
   /* Calling function using call by reference */
   getDoubleValue(&A);
   /* Any change in the value of formal parameter(F)
   will effect the value of actual parameter(A) */
   printf("A(Actual Parameter) = %d\n", A);
   
   return 0;
}
Above program find sum of all integers from 1 to N, using while loop.

Output
Enter a numbers
7
F(Formal Parameter) = 9
A(Actual Parameter) = 9

Above program shows that, If we call a function using call by reference then any change in the value of formal parameters will change the value of actual parameter. Both actual and formal parameters are pointing to same memory location.


Default Arguments

C does not natively support default arguments like some other programming languages. However, you can simulate default arguments using function overloading or by providing multiple function prototypes with different parameter lists.

#include <stdio.h>

// Function prototypes
void displayInfo(char *name, int age, char *city);
void displayInfoWithDefault(char *name, int age);

int main() {
    displayInfo("John", 25, "New York");
    displayInfoWithDefault("Jane", 30);

    return 0;
}

// Function definition
void displayInfo(char *name, int age, char *city) {
    printf("Name: %s, Age: %d, City: %s\n", name, age, city);
}

// Function definition with default argument
void displayInfoWithDefault(char *name, int age, char *city) {
    printf("Name: %s, Age: %d, City: %s\n", name, age, city);
}

// Function prototype with default argument
void displayInfoWithDefault(char *name, int age);

In this example, the displayInfo function takes three arguments (name, age, and city). To simulate default arguments, a second function displayInfoWithDefault is introduced with only two arguments. This allows the caller to provide only the necessary arguments, and the function uses default values for the unspecified ones.


Variable Arguments

C provides a mechanism for functions to accept a variable number of arguments using the header. This is commonly used in functions like printf and scanf. The key functions are va_start, va_arg, and va_end.

#include <stdio.h>
#include <stdarg.h>

// Function prototype
double average(int num, ...);

int main() {
    printf("Average: %lf\n", average(3, 2.5, 3.5, 4.5));
    printf("Average: %lf\n", average(5, 1.0, 2.0, 3.0, 4.0, 5.0));

    return 0;
}

// Function definition
double average(int num, ...) {
    va_list args;
    double sum = 0.0;

    // Initialize the va_list
    va_start(args, num);

    // Sum all the arguments
    for (int i = 0; i < num; ++i) {
        sum += va_arg(args, double);
    }

    // Clean up the va_list
    va_end(args);

    // Calculate and return the average
    return sum / num;
}

In this example, the average function takes a variable number of arguments. The va_list type is used to iterate through the arguments, and the va_start and va_end macros are used to initialize and clean up the va_list. The va_arg macro retrieves the next argument from the list.


Best Practices for Passing Function Arguments in C

When passing function arguments in C, code maintainability, efficiency, and clarity must all be taken into account. Using best practices facilitates the development of legible and robust code. The following are some suggested methods for sending function arguments in C:
  • Use Meaningful Variable and Parameter Names : Make certain that both variables and function parameters have descriptive names. Providing functions and their arguments with meaningful names improves code readability and facilitates comprehension by both the self and others regarding the function's objective.
    // Poor naming
    void calculate(int a, int b);
    
    // Improved naming
    void calculateSum(int num1, int num2);
    

  • Avoid Excessive Use of Global Variables : Reduce the utilization of global variables as function arguments to an absolute minimum. Global variables may result in more difficult-to-maintain and comprehend code.
    // Avoid
    int total;
    
    void calculateTotal(int num1, int num2) {
        total = num1 + num2;
    }
    
    // Prefer
    int calculateTotal(int num1, int num2) {
        return num1 + num2;
    }
    

  • Choose Appropriate Passing Mechanism : For small-sized structures and basic data types, pass by value is preferable. Pass by reference (utilizing pointers) is recommended for larger structures or situations in which it is necessary to make modifications to the original value.
    // Pass by value
    void modifyValue(int x) {
        x = x + 10;
    }
    
    // Pass by reference
    void modifyValueByReference(int *x) {
        *x = *x + 10;
    }
    

  • Const-Correctness : To prevent modification of a parameter's value within a function, employ the const qualifier. Const-correctness enhances the readability and security of code.
    // Without const-correctness
    void printMessage(char *message) {
        // Code that doesn't modify message
    }
    
    // With const-correctness
    void printMessage(const char *message) {
        // Code that doesn't modify message
    }
    

  • Default to Pass by Value, Optimize if Necessary :Assigning values to arguments initially, this is more straightforward and secure. Pass by reference should be utilized only when performance-critical.
    // Start with pass by value
    void processArray(int arr[], size_t size) {
        // Code that doesn't modify the array
    }
    
    // Optimize if necessary
    void processArrayOptimized(int *arr, size_t size) {
        // Code that modifies the array
    }
    


Conclusion

A foundational knowledge of passing function arguments in C is essential for developing code that is both efficient and modular. Each approach, be it pass by reference, pass by value, variable arguments, or default arguments, possesses distinct applications and benefits. By attaining proficiency in these principles, one can augment their aptitude for developing adaptable and effective functions in C.

By adhering to these recommended guidelines when passing function arguments in C, one can enhance the code's maintainability, readability, and efficiency.