5B. Mental Model of Functions


Hand simulation of function calls: frames

Here are three function definitions.

  // Sqr(x) returns x2.

  int sqr(int x)
  {
    return x*x;
  }

  // FourthPower(y) returns y4.

  int fourthPower(int y)
  {
    int s = sqr(y);
    int r = sqr(s);
    return r;
  }

  // Test(z) returns 2*z4.

  int test(int z)
  {
    int k = fourthPower(z);
    return 2*k;
  }

Let's see how expression test(3) is performed by a computer. First, a frame is created for it. A frame holds the values of parameters and any variables that are used in the body, as well as an indication of what the function is about to do next.

test
z = 3
at: int k = fourthPower(z);

Test needs to call fourthPower(3). The computer creates another frame for that. Now there are two frames. The rightmost frame is the one that is currently running. Frames to the left of that are waiting.

test
z = 3
at: int k = fourthPower(z);
fourthPower
y = 3
at: int s = sqr(y);

Now fourthPower needs to computer sqr(3). Yet another frame is created, and the three frames look like this.

test
z = 3
at: int k = fourthPower(z);
fourthPower
y = 3
at: int s = sqr(y);
sqr
x = 3
at: return x*x;

Sqr(3) computes 3*3 and returns 9. Now the frame for sqr is destroyed, and the computer returns to the frame for fourthPower, with s = 9.

test
z = 3
at: int k = fourthPower(z);
fourthPower
y = 3
s = 9
at: int r = sqr(s);

Now fourthPower needs to call sqr again. The computer creates another fram for sqr, but this time with x = 9.

test
z = 3
at: int k = fourthPower(z);
fourthPower
y = 3
s = 9
at: int r = sqr(s);
sqr
x = 9
at: return x*x;

Sqr returns 81, and its frame is destroyed. We have

test
z = 3
at: int k = fourthPower(z);
fourthPower
y = 3
s = 9
r = 81
at: return r;

FourthPower returns 81 and its frame is destroyed. Only one frame is left.

test
z = 3
k = 81
at: return 2*k;

Finally, test returns 162, and its frame is destroyed.


Scope

Each function call runs in its own frame and only has access to variables in that frame. Clearly, no function can use another function's variables.

Would it have made a difference whether the parameters were all called x, like the following?

  // Sqr(x) returns x2.

  int sqr(int x)
  {
    return x*x;
  }

  // FourthPower(x) returns x4.

  int fourthPower(int x)
  {
    int s = sqr(x);
    int r = sqr(s);
    return r;
  }

  // Test(x) returns 2*z4.
  int test(int x)
  {
    int k = fourthPower(x);
    return 2*k;
  }

Clearly not. Since no function can see any other function's variables, it does not make any difference if one function's parameter or variable has the same name as another's. You can have two households both having a resident named "Jackie". There is no confusion. And certainly, one household's "Jackie" is not the same person as another household's "Jackie".


Call by value

Look again at the above hand simulation using frames, and look at how parameters are passed. A parameter is just a variable whose value is set when the frame is created, according to the function call. We say that parameters are passed by value, since the value of the parameter in the call is put into the new frame.

What would happen if a function changed that variable, like this version of fourthPower does?

  // FourthPower(x) returns x4.

  int fourthPower(int x)
  {
    x = sqr(x);
    x = sqr(x);
    return x;
  }

Each change to x is done inside the frame for fourthPower. It has no effect on any other frame. It cannot change the variable called x in any other frame.

This new version of fourthPower might look appealing, since it uses fewer variables. In reality, changing the value of x is not a good idea. For one thing, once x is changed, the function no longer has access to the original value of the parameter. Also, changing a parameter tends to make programs more confusing.

The standards for this course require you not to change any parameter that is passed by value.

Later we will see another way of passing parameters that does allow you to change the value of the parameter in a sensible way.


Const parameters

Remember that you reduce the number of mistakes that you make by taking away opportunities to make mistakes. You can make it impossible to change a parameter that is passed by value by saying that it is const. For example, definitions

  // Sqr(x) returns x2.

  int sqr(const int x)
  {
    return x*x;
  }

  // FourthPower(y) returns y4.

  int fourthPower(const int y)
  {
    int s = sqr(y);
    int r = sqr(s);
    return r;
  }

  // Test(z) returns 2*z4.

  int test(const int z)
  {
    int k = fourthPower(z);
    return 2*k;
  }

prevent you from accidentally changing the value of a parameter.


More complicated expressions

Compare the following two definitions of fourthPower.

  int fourthPower(int y)
  {
    int s = sqr(y);
    int r = sqr(s);
    return r;
  }

  int fourthPower(int y)
  {
    return sqr(sqr(y));
  }  

There is really little difference between them, except that the second one is shorter. The only reason for introducing variables s and r was to make the hand simulation more clear. The C++ compiler converts the second version into something that looks more like the first one, but using special variable names that you cannot use directly, not s and r.


Summary

Functions are independent of one another. When a function is called, a new frame is created for it, holding the variables for that call. Only the function running that call can use the variables in its frame.


Exercises

  1. Using the original definitions of sqr, fourthPower and test, do a hand simulation of test(2). Answer

  2. Given the definitions below, what does A(2) return?

      int B(int x)
      {
        x++;
        return x*x;
      }
    
      int A(int x)
      {
        int y = B(x);
        return y*x;
      }
    
    Answer