Functions (Example 1)

Concepts:
Very simple function with arguments passed by value

Text:

Solution:

functions_1.c
/* 
   Write a function that receives as input two float numbers (a and b),
   it multiplies them and it returns the result of the multiplications.
   The function must be applied to a vector of name vet in order to multiplicate by 3.0
   each element of the vector.
   The result of the multiplication must be stored in a vector of name res with the same dimension
   of vet.
 */
 
 
#include <stdio.h>
#define SIZE 10
 
/* Prototype */
float mul(float a, float b);
 
int main(){
 
  float vet[SIZE] = {1.0, 5.0, 3.0, 7.0, 10.0, 1.0, 2.0, 3.0, 4.0, 5.0};
  float res[SIZE];
  int i;
 
  /* Multiplication of each element of vet by 3.0 and storing of the result in res */
  for(i=0; i < SIZE; i++){
    res[i] = mul(vet[i], 3.0);
  }
 
  /* Print results, i.e., the content of vector res */
  for(i=0; i < SIZE-1; i++){
    printf("%f ", res[i]);
  }
  printf("%f\n", res[SIZE-1]);
 
  return 0;
}
 
 
/* Implementation of the function mul that performs the multiplication between a and b */
float mul(float a, float b){
  float result;
 
  result = a*b;
 
  return result;
}
 
/* The function mul can be also implemented in the following way */
/*
float mul(float a, float b){
    return a*b;
}
*/

Comments:

/* Prototype */
float mul(float a, float b);

indicates that in the following will be used the function with name mul that receives as input parameters two variables of type float and it returns a variable of type float. The names of the parameters are not important for the compilation process, therefore this line of code can be substituted with the following one:

/* Prototype */
float mul(float, float);

Note: If you write the implementation of the function before its use, the declaration of the prototype is not needed. Indeed, the compiler, analyzing the float mul(float a, float b) part of the implementation of the function, has all the needed information, i.e., the name of the function, the type and the number of parameters and the type of the returned value. The compiler will use such information to check if the function is used in a correct way, in particular if the number and the type of parameters passed to it are consistent with its prototype.

int main(){
 
  float vet[SIZE] = {1.0, 5.0, 3.0, 7.0, 10.0, 1.0, 2.0, 3.0, 4.0, 5.0};
  float res[SIZE];
  int i;
 
  /* Multiplication of each element of vet by 3.0 and storing of the result in res */
  for(i=0; i < SIZE; i++){
    res[i] = mul(vet[i], 3.0);
  }
 
  /* Print results, i.e., the content of vector res */
  for(i=0; i < SIZE-1; i++){
    printf("%f ", res[i]);
  }
  printf("%f\n", res[SIZE-1]);
 
  return 0;
}

after the declaration and the initialization of the vector vet, and after the declaration of all the useful variables, the first for cycle performs the multiplication of all the element of vet by 3.0. Results are saved on the vector res.

res[i] = mul(vet[i], 3.0);

To perform the multiplication, the function mul is used. It multiplies its first parameter ris[i] for its second parameter 3.0. The function returns the result of the multiplication as a float number. The following line of code:

res[i] = mul(vet[i], 1.0+2);

would have obtained the same result, as well as the following line of code that does not make use of the function mul:

res[i] = vet[i]*3.0;

The second for cycle prints the content of the vector res.

/* Implementation of the function mul that performs the multiplication between a and b */
float mul(float a, float b){
  float result;

  result = a*b;

  return result;
}  

In practice, the values passed to the function (i.e., ris[i] and 3.0, are copied in the variables a and b of the function, respectively). It is important to notice that all the variable inside the function (i.e., a, b and result) are local to the function. Indeed, any time the function is executed, a memory space sufficient to store 3 variables is allocated. These variables can be used inside the compound of the function but, at the end of the function, they are deallocated and the memory reserved for them is released. Inside the function the variables a and b have the value ris[i] and 3.0 (note, a modification on the value of the variable a does NOT implies the modification of the value of the variable ris[i]). Inside the implementation of the function, it is declared a local variable with name result. The variable result is used to store the result of the multiplication of a and b. The command return is used to return the value of the variable result to the caller, i.e. the main program that has called the function mul(vet[i], 3.0). In practice, it is like the function mul(vet[i], 3.0) is substituted each time it is called with the result of the multiplication between vet[i] and 3.0. It is worth to notice that the main program is itself implemented like a function, the function with name int main(). The main function receives no parameter as input (a different way to write the prototype of this function is int main(void), where void identifies no type). At the end of the int main(), the last command is return 0;. Such a command indicates that the function returns to the caller the value 0. In the case of the int main() function, the caller is the operating system (Windows, Linux, Mac OS,…), and the value 0 returned to the operating system indicates that the program is terminated successfully (note from the prototype of the function int main() that it returns an integer number).

float mul(float a, float b){
    return a*b;
}

in this case it does not use the variable result to store the result of the multiplication between a and b, but it immediately returns the result of the operation without using a local variable (return a*b;).