Functions and Arguments in C
We have seen, previously, that a parameter of a function is a variable declared in the definition of a function, while an argument of a function is the actual value passed to the function when it is invoked.
Now we need to go into detail and study better how the C language manages the parameters and arguments of a function.
In particular, it is fundamental to understand the concept of pass by value of arguments and how the C compiler handles argument conversion.
Function Parameters and Arguments
Already previously, in the lesson on function definition, we introduced the concept of parameter and argument of a function.
Now we go into detail and review the difference, more than anything conceptual, between parameter and argument.
A parameter is a variable declared in the definition of a function. Parameters are used to pass information to the function. A parameter is a placeholder for a value that will be passed to the function when it is invoked.
An argument is the actual value that is passed to the function when it is invoked. Arguments are the values that are passed to the function when it is called.
A function argument can be the name of a variable or the result of an expression.
To clarify, let's take the definition of the sum function:
int sum(int x, int y) {
return x + y;
}
In this case, x and y are parameters of the sum function. When we invoke the sum function, we must pass two arguments that will be assigned to the parameters x and y.
int a = 10;
int result = sum(a, (5 * 2) + 3);
In the example above, a and (5 * 2) + 3 are arguments of the sum function. When the sum function is invoked, the value of a is assigned to x and the value of (5 * 2) + 3 is assigned to y. As can be noted, in one case the argument is the name of a variable, in the other it is the result of an expression.
Very often, in programming texts, reference is made to parameters and arguments as if they were the same thing. In reality, it is important to understand the difference between the two concepts to better understand how functions work in C language.
Pass by Value
An important detail concerning function arguments is pass by value.
In C language, function arguments are passed by value. This means that, when a function is invoked, a copy of the argument value is passed to the function. This copy is assigned to the corresponding parameter.
To clarify, let's take the increment function:
int increment(int x) {
return x + 1;
}
When we invoke the increment function, the value of the argument is copied into the parameter x. For example, in the code that follows:
int a = 10;
int b = increment(a);
The value of a (which is 10) is copied into the parameter x of the increment function. Therefore, the increment function will work with a copy of the value of a, not with the actual value of a.
The consequence is that any modification made to a function parameter will not affect the value of the original argument. For example:
int increment(int x) {
x = x + 1;
return x;
}
int a = 10;
int b = increment(a);
printf("a: %d\n", a); // Prints: a: 10
printf("b: %d\n", b); // Prints: b: 11
Although the increment function modifies the value of the parameter x, the value of a remains unchanged.
Pass by value is an important concept and must always be kept in mind when working with functions in C language.
Pass by Value
In C language, all function arguments are passed by value. This means that a copy of the argument value is passed to the function. Any modification made to the parameters of a function will not affect the value of the original argument.
Function parameters behave, effectively, as variables initialized with the value of the corresponding argument.
The fact that arguments are passed by value has advantages and disadvantages.
A first advantage is that, since a function parameter can be modified without affecting the passed argument, we can use parameters as variables within the function thus reducing the quantity of auxiliary variables we need.
For example, suppose we want to implement a function that calculates the power of an integer number. We can write the function in this way:
int power(int base, int exponent) {
int result = 1;
for (int i = 0; i < exponent; i++) {
result *= base;
}
return result;
}
However, since the parameter exponent is nothing more than a copy of the value of the passed argument, we can modify it without affecting the original argument. Therefore, we can rewrite the function without the need to use a variable i, in this way:
int power(int base, int exponent) {
int result = 1;
while (exponent-- > 0) {
result *= base;
}
return result;
}
In this way, we have eliminated the variable i and directly used the parameter exponent as a counter for the while loop.
The main disadvantage of pass by value is that, given that the C language admits only one return value, we cannot write functions that return more than one value. However, this limitation can be overcome through the use of pointers that we will see in the next lessons.
Argument Conversion
The C compiler, in general, expects that the type of arguments passed to a function corresponds to the type of the function's parameters. If the type of arguments does not correspond to the type of parameters, the compiler will generate an error.
This is not, however, always true. In some cases, the C compiler is able to automatically convert the arguments passed to a function into the type required by the parameters.
The conversion rules depend on whether the compiler, when it finds the call of a function, has already encountered the prototype of the function itself.
-
If the compiler has encountered the function prototype, then it is able to automatically convert the arguments passed to the function into the type required by the parameters. This is possible because the compiler already knows how to convert the arguments correctly.
For example, if we have the prototype of the
sumfunction:int sum(int x, int y);The compiler knows that
sumrequires two arguments of typeint. If we pass to the function two arguments of typefloat, the compiler will be able to automatically convert thefloatvalues toint.float a = 10.5; float b = 20.3; int result = sum(a, b);In this case, the compiler will convert the
floatvaluesaandbtointbefore passing them to thesumfunction. -
If the compiler has not encountered the function prototype then the situation depends on whether the compiler respects the C89 standard or supports the standard from C99 onwards.
-
C89 Standard: if the compiler respects the C89 standard, then it performs an implicit conversion:
- A
floatargument is converted to adouble; - Any other type is converted to an
int(where possible).
- A
-
C99 Standard and later: if the compiler supports the C99 standard or later, then it generates an error.
-
Always use prototypes and never rely on implicit conversion
To avoid errors and unexpected behaviors, it is always better to declare function prototypes and ensure that the type of passed arguments corresponds to the type of function parameters.
In Summary
In this lesson we have delved into the concepts of parameter of a function and argument of a function that we had already introduced in previous lessons.
We have seen that a parameter is a variable declared in the definition of a function, while an argument is the actual value passed to the function when it is invoked.
We have also discussed the pass by value of arguments in C language. Arguments are passed by value, which means that a copy of the argument value is passed to the function. This has implications on the behavior of functions and on modifications to parameters.
Finally, we have talked about argument conversion. The C compiler is able to automatically convert arguments passed to a function into the type required by the parameters, but it is always better to declare function prototypes to avoid errors and unexpected behaviors.
In the next lesson we will delve into the case where we want to pass an array as an argument to a function.