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

From Embedded Systems Learning Academy
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.

You can view the following screencast to review this lesson, but should read through this material regardless :

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 = 0;

    // Pointer can only be set equal to an address, so :
    // Set it equal to address of my_int_one by using & (address) operator
    my_int_ptr = &my_int_one;

    // * means to dereference
    // Modify the memory pointed by my_int_ptr which will write to address of my_int_one:
    *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 dereferencing 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)
{
   /*********
    * Two things to note :
    *   - intInput points to address of main()'s anInt
    *   - fpInput  points to address of main()'s aFloat
    *
    *  We can modify main()'s variables using these pointers 
    */
   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.

float getSumOfFloatingPointArray(float *floatArray, int elements);

int main(void)
{
   float samples[4] = {1.1, 2.2, 3.3, 4.4};
   float sum = getSumOfFloatingPointArray(samples, 4);
   printf("Sum = %f\n", sum);
}

float getSumOfFloatingPointArray(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];
   }

   return sum;
}

Notice that when main() calls the getName() and getSumOfFloatingPointArray() 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. Use all applicable functions from previous assignment if possible, but modify the getAvg() function.
    • The parameters of this function should be pointer to an array, and the size of the array.
    • Returns the average of the array.
  2. Build a function called getExamScoreFromUser()
    • Ask the user to enter the exam score
    • scanf() into a local variable
    • return the number the user entered.
  3. Build and present a menu to a user like the following and enclose it into a loop that ends when the Quit option is chosen.
    1. Enter user name.
    2. Enter exam scores.
    3. Display average exam scores.
    4. Display summary.
    5. Quit
  4. For choice 1, scanf a string, and store it to a username char array.
  5. For choice 2, do the following :
    • Ask the user how many exams there are. Only ask the user this number of exam scores to enter
      Hint: Declare an integer array of 100, and some array members will be left un-used which is okay
      If the user wants to enter more than the array size we declared, display an error, and have the user re-entry the exam count.
    • Use a for loop in combination with getExamScoreFromUser() to get your exam score.
      Hint: You can use this inside your for loop: scores_array[i] = getExamScoreFromUser()
  6. For choice 3, do the following :
    • If the user has already entered exam scores, use the getAvg() function to get the average of the exam scores.
    • If the user has not yet entered exam scores, display an error message similar to: "Please use the menu to enter exam scores first"
  7. For choice 4, if the user has not yet entered their name and exam scores, display an error message. Otherwise display the average, the letter grade of the average, and the user's name.
    Example: "Hello Preet, your exam scores were 80, 80, 90, 90, 100, 100. Your average is 90.0 with letter grade: A"
    Hint: You will need to print "Hello Preet, your exam scores were " first, and then use for loop to print the exam scores, followed by ". Your average is 90.0 with letter grade: A"
    Use the getAvg() and getGradeLetter()where possible.
  8. When the Quit option is chosen, end the primary loop that contains the menu.
  9. Insert a line with your favorite character before printing the menu.


int getScore(int num);

int main(void)
{
    int scores_array[10];
    int num_scores = 0;

    printf("How many exam scores are there?");
    scanf("%i", &num_scores);

    // TODO: Do not proceed if num_scores is greater than 10
    //       because we only declared memory for 10 exam scores.
    for (int i=0; i < num_scores; i++) {
        scores_array[i] = getScore(i+1);
    }

    return 0;
}
int getScore(int num)
{
    int score = 0;
    printf("Enter score for exam #%i: ", num);
    scanf("%i", &score);
    return score;
}

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?