Difference between revisions of "ES101 - Lesson 8 : Functions with Pass-By-Reference"

From Embedded Systems Learning Academy
Jump to: navigation, search
(Basics of Pointers)
(Basics of Pointers)
 
(21 intermediate revisions by the same user not shown)
Line 2: Line 2:
 
You will learn the basics of pointers, which are used in combination with the functions.
 
You will learn the basics of pointers, which are used in combination with the functions.
  
Pass-by-value type of functions pass just the value of the variables into a function, but with a simple change, you can use functions that accept pass-by-reference type of variables. This is a very powerful concept that will allow your functions to change the values of the parameters.
+
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 :
 +
*  '''VIDEO : [http://www.youtube.com/watch?v=kQPN5dA5t8A Pointers in C/C++]'''
 +
*  '''VIDEO : [http://www.youtube.com/watch?v=OlXEQaL8JT4 Pointer use in functions in C/C++]'''
 +
*  '''VIDEO : [http://www.youtube.com/watch?v=GyA2b7PHUlU Pointer to arrays passed to functions in C/C++]'''
  
 
== Basics of Pointers ==
 
== Basics of Pointers ==
Line 13: Line 18:
  
 
     // A pointer is declared by using the *
 
     // A pointer is declared by using the *
     int* my_int_ptr;
+
     int *my_int_ptr = 0;
  
     // Point the my_int_ptr to the memory address of my_int_one.  Note the & operator
+
     // 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;
 
     my_int_ptr = &my_int_one;
  
     // By using the *, we dereference the memory of my_int_ptr and write 1 to it:
+
     // * means to dereference
 +
    // Modify the memory pointed by my_int_ptr which will write to address of my_int_one:
 
     *my_int_ptr = 1;
 
     *my_int_ptr = 1;
  
Line 25: Line 32:
  
 
     // Let's modify my_int_two indirectly:
 
     // Let's modify my_int_two indirectly:
     my_int_ptr = &my_int_two;
+
     my_int_ptr = &my_int_two; // <-- Change pointer location
     *my_int_ptr = 2;
+
     *my_int_ptr = 2;         // <-- Change value at the pointed location
  
 
     // This will print 2:
 
     // This will print 2:
Line 35: Line 42:
 
*  A pointer is declared by using the * right before the variable name
 
*  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 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.
+
*  A pointer's pointed value can be changed by dereferencing it.
  
 
<BR/>
 
<BR/>
Line 64: Line 71:
 
void foobar(int *intInput, float *fpInput)
 
void foobar(int *intInput, float *fpInput)
 
{
 
{
   // intInput and fpInput are addresses, so to read/write
+
   /*********
  // the values at these addresses, we must use * (dereference)
+
    * Two things to note :
  // operator
+
    *  - 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);
 
   printf("Input was: %i, %f\n", *intInput, *fpInput);
  
Line 78: Line 89:
  
 
==Passing Arrays to a Function==
 
==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 still pass the address of its first element. Arrays usually consist of large number of elements; therefore, passing array pass-by-value would be time consuming because each elements needs to copied. To address this issue, the address of first element of the array is passed to functions (pass-by-reference). See the examples below for illustration.
+
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.
  
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
void getName(char *charArray);
+
float getSumOfFloatingPointArray(float *floatArray, int elements);
float getAverageOfFloatingPointArray(float *floatArray, int elements);
 
  
 
int main(void)
 
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 samples[4] = {1.1, 2.2, 3.3, 4.4};
   float average = getAverageOfFloatingPointArray(samples, 4);
+
   float sum = getSumOfFloatingPointArray(samples, 4);
   printf("Average = %f\n", average);
+
   printf("Sum = %f\n", sum);
}
 
 
 
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)
+
float getSumOfFloatingPointArray(float *floatArray, int elements)
 
{
 
{
 
   int i = 0;
 
   int i = 0;
Line 108: Line 107:
 
   for(i = 0; i < elements; i++)
 
   for(i = 0; i < elements; i++)
 
   {
 
   {
 +
      // Use ordinary array index method to access/modify the array
 
       sum += floatArray[i];
 
       sum += floatArray[i];
 
   }
 
   }
  
   float average = sum / elements;
+
   return sum;
  return average;
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Notice that when <code>main()</code> calls the <code>getName()</code> and <code>getAverageOfFloatingPointArray()</code> 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. The one problem is that <code>getAverageOfFloatingPointArray()</code> will not know how big the array is, so another parameter, <code>elements</code>, is given, such that this function can know how many elements there are in the array. Again, this is similar to pass-by-reference, except how the arrays are passed. One last thing to note is that when the <code>getName()</code> function tried to do <code>scanf</code>, it did not need to pass the address of <code>charArray</code>, because the <code>charArray</code> is the address to begin with. Furthermore, notice that the <code>floatArray</code> was accessed simply by using index locations.
+
Notice that when <code>main()</code> calls the <code>getName()</code> and <code>getSumOfFloatingPointArray()</code> 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==  
 
==Assignment==  
# Build the following functions and use meaningful names for each function.
+
 
#*<b>1a.</b> A function asks to input an integer, scans it and returns the input data.
+
# Use all applicable functions from previous assignment if possible, but modify the <B><code>getAvg()</code></B> function.
#*<b>1b.</bA function asks to input a floating-point number, scans it and returns the input data.
+
#*  The parameters of this function should be pointer to an array, and the size of the array.
#*<b>1c.</b> A function that takes a char pointer to a char array, asks for the user‟s first name, and stores it into the array using the pointer.
+
#* Returns the average of the array.
#*<b>1d.</bA function that takes two integer pointers and swaps the numbers.
+
#  Build a function called <B><code>getExamScoreFromUser()</code></B>
# Ask the user to input two integers using the function built in 1a, swap them using your swap function, and print the values on the screen.
+
#* Ask the user to enter the exam score
# Declare an array of 5 floating point numbers, ask the user to input five floating point numbers by calling the function in 1b five times, and store the value to the five index locations of the floating-point array. Create a function that can calculate the average of floating-point array and returns the average. Using this function, get the average of the user input and print it out.
+
#*  scanf() into a '''local''' variable
# Ask the user to input their name using the function built in 1c and print the name using a for loop in capital letters.
+
#*  return the number the user entered.
 +
#  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.
 +
##  Enter user name.
 +
##  Enter exam scores.
 +
##  Display average exam scores.
 +
##  Display summary.
 +
##  Quit
 +
# For choice 1, scanf a string, and store it to a username char array.
 +
#  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 <B><code>getExamScoreFromUser()</code></B> to get your exam score.
 +
#*:  Hint: You can use this inside your for loop: <B><code>scores_array[i] = getExamScoreFromUser()</code></B>
 +
# For choice 3, do the following :
 +
#*  If the user has already entered exam scores, use the <B><code>getAvg()</code></B> 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"
 +
#  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 <B><code>getAvg()</code></B> and <B><code>getGradeLetter()</code></B>where possible.
 +
# When the Quit option is chosen, end the primary loop that contains the menu.
 +
#  Insert a line with your favorite character before printing the menu.
 +
 
 +
 
 +
<syntaxhighlight lang="c">
 +
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;
 +
}
 +
</syntaxhighlight>
  
 
==Questions==
 
==Questions==

Latest revision as of 23:10, 19 July 2013

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?