Function Calls and Argument Conversions
Function Calls
A function is called with actual arguments placed in the same sequence as their matching formal parameters. Use the function-call operator ()
:
function_name(expression_1, ... , expression_n)
Each expression
in the function call is an actual argument. Number and types of actual arguments should match those of formal function parameters. If types do not match, implicit type conversions rules will be applied. Actual arguments can be of any complexity, but order of their evaluation is not specified.
Upon function call, all formal parameters are created as local objects initialized by the values of actual arguments. Upon return from a function, a temporary object is created in the place of the call, and it is initialized by the expression of the return
statement. This means that the function call as an operand in complex expression is treated as a function result.
If the function has no result (type void
) or the result is not needed, then the function call can be written as a self-contained expression.
In C, scalar arguments are always passed to the function by value. The function can modify the values of its formal parameters, but this has no effect on the actual arguments in the calling routine. A scalar object can be passed by the address if a formal parameter is declared as a pointer. The pointed object can be accessed by using the indirection operator *
.
// For example, Soft_UART_Read takes the pointer to error variable, // so it can change the value of an actual argument: Soft_UART_Read(&error); // The following code would be wrong; you would pass the value // of error variable to the function: Soft_UART_Read(error);
Argument Conversions
If a function prototype has not been previously declared, the mikroC PRO for PIC32 converts integral arguments to a function call according to the integral widening (expansion) rules described in Standard Conversions. If a function prototype is in scope, the mikroC PRO for PIC32 converts the passed argument to the type of the declared parameter according to the same conversion rules as in assignment statements.
If a prototype is present, the number of arguments must match. The types need to be compatible only to the extent that an assignment can legally convert them. The user can always use an explicit cast to convert an argument to a type that is acceptable to a function prototype.
If you create a library of routines with the corresponding header file of prototypes, consider including that header file when you compile the library, so that any discrepancies between the prototypes and actual definitions will be detected.
The compiler is also able to force arguments to change their type to a proper one. Consider the following code:
int limit = 32; char ch = 'A'; long res; // prototype extern long func(long par1, long par2); main() { ... res = func(limit, ch); // function call }
Since the program has the function prototype for func
, it converts limit
and ch
to long
, using the standard rules of assignment, before it places them on the stack for the call to func
.
Without the function prototype, limit
and ch
would be placed on the stack as an integer and a character, respectively; in that case, the stack passed to func
will not match size or content that func
expects, which can cause problems.
What do you think about this topic ? Send us feedback!