ES101 - Lesson 8 : Functions with Pass-By-Reference

From Embedded Systems Learning Academy
Revision as of 18:24, 24 October 2012 by Preet (talk | contribs) (Assignment)

Jump to: navigation, search

Objective

You will learn the basics of pointers, which are used in combination with the functions.

When functions pass a parameter by value, they provide copies of their variables that get duplicated as parameters of a function. By definition, the original variable(s) provided remain unaffected if their copies are modified. The alternate to "pass-by-value" is "pass-by-reference" which will actually provide original variable's address so that the function may alter the values. More specifically, instead of a function taking a variable as parameter, the function will take a pointer to the variable as parameter.

Basics of Pointers

So far, pass-by-value is what you've used when you designed your functions to take inputs as parameters. Pass-by-reference passes a variable's memory address instead of the variable's value; therefore, you can change the data at that memory address hence changing the input value. Let's start with the basics of a pointer:

void main()
{
    int my_int_one = 0;
    int my_int_two = 0;

    // A pointer is declared by using the *
    int *my_int_ptr;

    // Point the my_int_ptr to the memory address of my_int_one.  Note the & operator
    my_int_ptr = &my_int_one;

    // By using the *, we dereference the memory of my_int_ptr and write 1 to it:
    *my_int_ptr = 1;

    // Without using my_int_one, we modified it to be the value of 1
    printf("my_int_one = %i\n", my_int_one);

    // Let's modify my_int_two indirectly:
    my_int_ptr = &my_int_two; // <-- Change pointer location
    *my_int_ptr = 2;          // <-- Change value at the pointed location

    // This will print 2:
    printf("my_int_two = %i\n", my_int_two);
}

Let's summarize important points:

  • A pointer is declared by using the * right before the variable name
  • A pointer can point to different memory locations by setting it to & (address) of another variable
  • A pointer's pointed value can be changed by referencing it.


Pass By Reference

Pass by reference provides a mean to provide input to functions by specifying a memory location, which allows the function to modify caller's variables. In other words, instead of just merely giving a value, you pass the pointer of variable(s), and the pointer points to the memory address where the data is stored. The values are then accessed by using the * operator inside the function, which is called the dereference operator. Students often get confused with & and * symbols. & refers to the memory address whereas as * refers to a value stored at this memory address.

// Declare foobar() that takes pointers of int and float
void foobar(int *intInput, float *fpInput);

int main(void)
{
   // We can no longer do this since we have to provide
   // addresses of some variable
   foobar(1, 1.2);        // ERROR!
   foobar(anInt, aFloat); // ERROR!

   // Pass variable addresses to foobar()
   int anInt = 1;
   float aFloat = 2.3;
   foobar(&anInt, &aFloat);

   // After function call to foobar(): anInt and aFloat are zero
   printf("Variables now: %i, %f\n", anInt, aFloat);
}

void foobar(int *intInput, float *fpInput)
{
   // intInput and fpInput are addresses, so to read/write
   // the values at these addresses, we must use * (dereference)
   // operator
   printf("Input was: %i, %f\n", *intInput, *fpInput);

   // Modify the inputs:
   *intInput = 0;
   *fpInput  = 0;
}

Similar to how you provided address to scanf function, you must provide address of variables to foobar(), when calling the function otherwise a compiler error will be encountered as seen by first two calls to foobar() in the figure above. The third call to foobar() shows correct function call, which passes addresses of anInt and aFloat variables. The main() function called foobar() with values as 1 and 2.3, and, after foobar() returns, the values of the variables are set to 0 by foobar().

Passing Arrays to a Function

Passing arrays to a function is not much different than passing a pointer. This is because to pass an array to a function, you actually pass the address of its first element. Since the address of the first element is available, any element of the array can be accessed by offsetting its starting address. See the examples below for illustration.

void  getName(char *charArray);
float getAverageOfFloatingPointArray(float *floatArray, int elements);

int main(void)
{
   char userName[50];
   getName(userName);
   printf("Your name is: %s\n", userName);

   float samples[4] = {1.1, 2.2, 3.3, 4.4};
   float average = getAverageOfFloatingPointArray(samples, 4);
   printf("Average = %f\n", average);
}

void getName(char *charArray)
{
   printf("Enter your name: ");
   // No & symbol needed since the pointer is already the address
   scanf("%s", charArray);
}

float getAverageOfFloatingPointArray(float *floatArray, int elements)
{
   int i = 0;
   float sum = 0;
   for(i = 0; i < elements; i++)
   {
      // Use ordinary array index method to access/modify the array
      sum += floatArray[i];
   }

   float average = sum / elements;
   return average;
}

Notice that when main() calls the getName() and getAverageOfFloatingPointArray() functions, it just passes the arrays without the & operator; this is because the array name itself is the address of the first element of the array.

When you pass an array to a function, the function needs to know the number of elements that belong to the array. Without this information, the function would not know the size of the array memory allocation. Once an array is passed to a function, the function can access it just like normal array indexed elements because the index acts like an offset of the base address of the array.

Assignment

  1. Declare and define the following functions:
    • 1a. getInt()  : Use printf/scanf INSIDE this function to get an integer, and return it.
    • 1b. getFloat() : Use printf/scanf INSIDE this function to get a float, and return it.
    • 1c. getName(char* name) : Use printf/scanf INSIDE this function, and store to the parameter pointer.
    • 1d. swap(int* a, int* b): takes two integer pointers and swaps the numbers at the memory pointed by the pointers.
    • 1d. getAverage() : You decide the parameters required to calculate average of an array. This should return the computed average back without any printf taking place inside this function.
  2. Note that functions in 1a, 1b, 1c, and 1d should work with the sample code below.
  3. Finish the sample code (note the TODOs) and run the program :
    • TODO 1: Call the swap function and print the swapped values
    • TODO 2 and TODO 3: Declare array of float and get input to the array using getFloat()
    • TODO 4: Call getAverage() to get average, and output the average to the user.
int main(void)
{
    // Get the name from the user :
    char name[32];
    getName(name);

    int my_int_one = getInt();
    int my_int_two = getInt();
    
    // TODO 1 : Call your swap() function to swap values of my_int_one, and my_int_two
    //          Then print the values of my_int_one and my_int_two

    // TODO 2 : Declare array of 5 floating-point numbers

    // TODO 3 : Use for loop to get input and store to the array elements :
    for(int i=0; i<5; i++) {
        array[i] = getFloat();
    }

    // TODO 4 : Call getAverage function to get the average
    // then store this to a variable and print it out.
    float average = getAverage( <fill in the code >);

    return 0;
}

Questions

Answer the following questions to complete the lab.

  1. Although pass-by-reference can be used to substitute all pass-by-value functions, why is pass-by-value be considered safer?
  2. When passing an integer array to a function, why do you also need to pass the number of elements in that array?