A Robot Model
of Function
Behavior in C/C++
John C. Molluzzo
Pace University
Two of the most difficult topics for students in C and C++ courses
are functions
and pointers. Though many books do a good job discussing pointers, few
address
the problems beginning students have when first encountering the
function concept.
Students usually have some difficulty understanding:
- how a function is executed by a calling program;
- how to use a function that returns void and a function that
returns a value;
- how arguments are passed by value;
- how return values are passed back to the calling function;
- how parameters, local variables, static local variables, and
global variables
are treated by functions;
- how arguments are passed by pointers; and
- how (in C++) arguments are passed by reference.
This article discusses function behavior through a model that uses a
robot
and its specially constructed environment. Being a model, it does not
perfectly
reflect every aspect of function behavior. However, it does model most
of the
basic and important aspects of functions and argument passing. I have
used the
model very successfully in teaching functions in both C and C++
courses to undergraduate
and graduate students as well as professional programmers in a
workshop environment.
Throughout the article, I shall distinguish between the terms
argument
and parameter. A function argument (sometimes called an
actual
parameter) is an actual value passed to a function in the function
call. A parameter
(sometimes called a formal parameter) is a variable declared in the
function's
definition header that the function uses to store the corresponding
argument
value supplied by the calling function in the function call.
The Basis of the
ModelThe
Function Robot
Figure 1 shows the basis of this model. The computer microprocessor
is modeled
by a robot, CPU, pronounced "CeePoo," which carries out program
instructions.
A function is modeled by a room, which houses variables and
instructions. The
robot can move from room to room. CPU can also carry things as it
travels from
one room to the next. (More about what kinds of things the robot can
carry later.)
CPU, when in a function room, carries out the function's instructions.
A function
room contains a door through which the robot can pass. When in a
function room,
CPU cannot see into any other room. If a variable is declared inside
the functionsay
int
nthe robot
retrieves a storage box from main memory and places a label with the
variable's
name on the outside of the box (i.e., the robot allocates storage for
the variable).
Such a box represents a local automatic variable. If the function
requires a
parametersay int ithe
parameter is treated as an ordinary variable. Thus, parameters are
also modeled
as boxes inside the function room. The only difference between a
parameter and
an ordinary variable is that CPU gives a value to a parameter upon
entering
the function room. Therefore, I depict a parameter at the entrance to
the door.
The value the robot gives to the parameter is determined by how the
function
is called.

Figure 1.
All functions are modeled in this way, including the
function
main(). Thus,
virtually
every C/C++ program involves using at least two functions: main()
and one or more library or user-defined functions.
A First ExamplePassing Arguments by
Value
To show how the basic model works, let's consider a simple C++
program that
uses one user-defined function: Add().
Here is the program code:
// Program1.cpp
#include <iostream.h>
int Add(int, int);
int main()
{
int n1, n2, sum;
cout << "Enter a number: ";
cin >> n1;
cout << "Enter another number: ";
cin >> n2;
sum = Add(n1, n2);
cout << "The sum is " << sum;
return 0;
}
int Add(int i1, int i2)
{
int sum;
sum = i1 + i2;
return sum;
}
Figure 2 shows the two function rooms and the other components of the
Robot
ModelMainStorage, the cin
input conveyor belt, and the cout
output conveyor belt.

Figure 2.
MainStorage, which is accessible to CPU from any function room,
contains storage
boxes. Each box is capable of storing a single value. The boxes are of
different
typesinteger, float, double, character, and so on. When a
function declares
a variable, the robot takes a storage box of the appropriate type from
MainStorage,
brings it into the function room, and labels it with the name of the
variable.
If the variable is initialized in its declaration, the robot
immediately empties
the box of any garbage value it might contain from a previous use and
places
the initial value into the box.
The cout
conveyor
belt is available to CPU from any function room. If the robot executes
a cout
statement, it places the data indicated by the statement onto the
cout
conveyor belt. The data is then sent to the monitor for display.
Similarly,
if the robot executes a cin
statement, it waits for data to come down the cin
conveyor belt. It retrieves this data and places a copy of it into the
variable
indicated in the cin
statement.
To show these elements in action, I discuss the behavior of Program1.cpp.
Before executing the instructions in main(),
CPU sees the function prototypes. These tell the robot which functions
will
be used in the program, the types of their arguments, and how their
arguments
should be passed. In Program1.cpp,
the prototype tells the robot that function Add()
has two integer arguments that are passed by value. To begin the
program, CPU
enters the main() room
and creates three variablesn1,
n2, and sumby
taking three integer boxes from MainStorage and labeling them with the
variable
names. It then places the string "Enter a number" onto the cout
conveyor for display on the monitor. This prompts the user to enter a
number.
Let's assume that the user enters 14. To execute the cin
statement, the robot waits for a number to arrive on the cin
conveyor so it can be placed into the variable box n1.
When the number 14 arrives, CPU empties any value that might be in the
n1
variable box and places 14 into that box. In the same manner, the
robot prompts
for and obtains a number (let's assume 45) to place into the n2
variable box.
The next statement CPU executes is the assignment that places a value
into
the variable sum. The
value it must place into sum
is the value of the function call Add(n1,
n2). To call the function, the robot first copies the value of
the first
function argument onto a slip of paper and copies the value of the
second function
argument onto a second slip of paper. Then, CPU takes the two slips of
paper
and leaves the room main()
to execute the function Add().
Because CPU leaves the room main()
before the function ends (the return
statement), all the variables in the room are left as they are.
CPU now enters the room Add()
and begins executing the instructions of that function. The first
thing the
robot must do is give values to the functions parameters. As CPU
enters
the room, it retrieves an integer storage box from MainStorage, labels
it i1,
and places the value that it wrote on the first slip of paper (14)
into the
box. In the same way, CPU creates the second parameter, i2,
and places the second value (45) into the box.
This method of transferring arguments to a function is called
passing arguments
by value. The values of the arguments are used, not the
original
variables themselves (n1
and n2, in
this case).
Therefore, any changes to the parameters i1
and i2 do not
affect
the values of n1 and
n2 back in
main().
Even if numeric constants are passed as an arguments, say Add(4,
7), CPU makes copies of the argument values (4 and 7) and
brings the
copies to the room Add().
Next, the robot creates the variable sum
by taking an integer box from MainStorage and labeling it sum.
Because this box labeled sum
is in a different room from the box labeled sum
in the room main(),
CPU cannot mix up them up. Whatever it does to the sum
box in the Add() room
has no effect on the sum box in room main().
The next statement the robot executes is the assignment. It adds the
values
that are in the parameters i1
and i2 and
places the
total (59) into the variable sum.
Then CPU executes the return
statement. To execute this statement, the robot writes the value of
the expression
that follows the word return
onto a slip of paper to take when it leaves the room. It then must
clean up
after itself, so it removes the labels from all the variables and
parameters
in the room, and returns the boxes to MainStorage. Thus, the
parameters and
any variables declared inside the function no longer exist. Finally,
CPU leaves
the room and returns to the room it left, main(),
to resume where it left off.
When CPU returns to main()
it uses the value on the slip of paper it is carrying, 59, as the
value of the
function call in the assignment. Thus, the robot places 59 into the
variable
sum. Finally,
to execute
the last cout
statement,
the robot places the string "The sum is " followed by the value of the
variable
sum onto the
cout
conveyor. When CPU executes the return
statement in main(),
it removes all labels from the variables declared in
main(), returns the boxes to MainStorage, writes 0 on a slip of
paper,
and returns to execute a statement in an operating system program.
Passing Arguments by
Pointer
Pointers are probably the most difficult concept that beginning C/C++
programmers
encounter. Following is a way of looking at pointers and their use in
function
calls that fits into the robot model.
Pointers
A pointer is a special type of storage box. Think of a pointer as a
remote
control box. The robot can attach one end of a remote control cable to
a pointer
box and the other end to another box. (The robot has an unlimited
supply of
cables.) The robot can use the controls on the pointer box to
manipulate the
box that the other end of the cable is attached to. A pointer's cable
is specific
to a particular data type. Thus, an integer pointer's cable can only
be attached
to an integer box; a character pointer's cable can only be attached to
a character
box, and so on. (The only way an integer pointer can be plugged into a
box of
a different type is to use a special adapter called a type cast.) To
illustrate,
the following declarations produce the situation shown in Figure
3.
int i = 5;
int j = 7;
int* i_ptr = &i;
int* j_ptr;

Figure 3.
When CPU executes these statements, it first creates two ordinary
integer variables
labeled i and
j,
as described previously. The data type in the third declaration, int*,
tells the robot that it must retrieve an integer pointer box from
MainStorage.
The robot does so and labels the box i_ptr.
The pointer initialization means that the robot attaches i_ptr's
remote control cable to the box labeled i.
Finally, the fourth declaration tells the robot to retrieve an integer
pointer
box from storage and label it j_ptr.
Note that this pointer is not initialized at this time. Therefore, it
is not
attached to any box.
Suppose that now the robot is instructed to carry out the following
statement.
*i_ptr = 9;
This tells the robot to use the remote controls of the pointer i_ptr
to place the integer 9 into the box that the pointer is connected to.
Thus,
the value 9 is placed into the box labeled i
(Figure 4).

Figure 4.
The following statement instructs the robot to attach j_ptr's
cable to the same box that i_ptr's
cable is attached to (Figure 5).
j_ptr = i_ptr;

Figure 5.
Pointers and Functions
To show how pointers are passed to functions, I discuss Program2.cpp,which
uses a standard swap function to interchange the values of two integer
variables.
// Program2.cpp
#include <iostream.h>
void Swap(int*, int*);
int main()
{
int i = 3;
int j = 5;
cout << "Before Swap: i = " << i <<
" and j = " << j << endl;
Swap(&i, &j);
cout << "After Swap: i = " << i << "
and j = " << j << endl;
return 0;
}
void Swap(int* a_ptr, int* b_ptr)
{
int temp;
temp = *a_ptr;
*a_ptr = *b_ptr;
*b_ptr = temp;
}
When main()
calls
Swap(), CPU
attaches
an integer remote control cable to each of the arguments it passes,
namely i
and j, and
then carries
the other ends of the cables to the Swap()
function room.
Once inside the room, the robot takes the first cable (which is
attached to
i) and
attaches it
to the first parameter, the pointer a_ptr.
Then it takes the second cable (which is attached to j)
and attaches it to its second parameter, the pointer
b_ptr. Thus, a_ptr
is a remote control for the variable i
and b_ptr is
a remote
control for the variable j
(Figure 6).

Figure 6.
When the robot executes the assignment
temp = *a_ptr;
it uses the remote controls of the pointer a_ptr
to make a copy of the contents, 3, of the box that a_ptr
is attached to, i back
in main(),
and places
this copy into its variable temp
(Figure 7).

Figure 7.
When the robot executes the second assignment
*a_ptr = *b_ptr;
it uses the remote control cable attached to b_ptr
to make a copy of the contents, 5, of the box to which b_ptr
is attached, j in main(),
and places that copy into the box to which a_ptr
is attached, i in main(),
using the remote cable attached to a_ptr.
Thus, the value of i
in main() is
changed
by the actions of the robot while executing in the function room Swap()
(Figure 8).

Figure 8.
Finally, the robot executes
*b_ptr = temp;
CPU, using the remote cable attached to b_ptr,
places a copy of its variable temp
into the box to which b_ptr
is attached, j in main().
This changes the value of j
in main()
(Figure 9).

Figure 9.
When the function ends, the robot detaches the cables attached to
a_ptr
and b_ptr and
returns
all the variable boxes that belong to Swap()
to MainStorage. It then carries the ends of the cables back to main().
When the robot returns to main(),
that function reactivates. CPU disconnects the remote cables that it
had attached
to i and
j.
Then the robot executes the next instruction of
main().
The net result of using Swap(),
then, is to interchange the values of the two variables in
main().
Functions That Return a Pointer
The following program, Program3.cpp,
discusses how the model works for a function that returns a pointer.
The program
replaces the first blank it finds in a string by a dollar sign. For
simplicity,
let's assume that the string contains at least one blank.
// Program3.cpp
//
// For simplicity, the program assumes
// that the string contains
// at least one blank.
#include <iostream.h>
char* FindBlank(char*);
int main()
{
char* words = "This is Program3.c";
char* c_ptr;
c_ptr = FindBlank(words);
*c_ptr = '$';
cout << words;
}
char* FindBlank(char* c_ptr)
{
while (*c_ptr != ' ')
++c_ptr;
return c_ptr;
}
A string is stored as a character array. To execute the first
declaration and
initialization in main(),
CPU must first create the initialization string and then create and
initialize
the character pointer words.
To compose the string constant used in the initialization of the
character pointer
words, the
robot counts
the number of characters in the string (18). It then retrieves that
number plus
one (19) of character storage boxes from MainStorage and glues them
together
in sequence. This collection of boxes is the character array. It then
places
the characters in the string into the boxes in the array one-by-one
and places
the null character in the last box. The declaration and initialization
of the
character pointer words
occurs as follows. CPU retrieves a character pointer box from
MainStorage, labels
it words, and
connects
a remote control cable from words
to the first character of the array that contains the character
string.
Therefore, when the robot calls FindBlank(words),
it passes the address of the first element of the array words.
In terms of the model, the robot attaches a second remote cable to the
box that
words is
attached to
(the first character in the string) and takes the other end to FindBlank().
Once in the function room FindBlank(),
the robot attaches the cable to c_ptr
(Figure 10).

Figure 10.
The while
loop in
FindBlank()
finds the
first blank in the string. The statement ++c_ptr;
moves the cable from the box to which it is attached to the next box.
When the
loop ends, the cable is attached to the first box in the string that
contains
a blank (Figure 11). To execute the return
statement, the robot detaches the cable from c_ptr,
returns the variable c_ptr
to MainStorage, and leaves the room FindBlank()
still carrying the end of the remote control cable. Remember that the
other
end of the cable, which is in the room main(),
is attached to the first blank in the string.

Figure 11.
When the robot resumes executing in main(),
it attaches the end of the cable it is carrying to the pointer c_ptr
that is in room main().
Therefore,
main()'s
c_ptr is now
attached
to the first blank in the string (Figure 12). The next statement in
main()
then places a $ at
the position containing the blank. Finally, CPU places a copy of the
string
onto the cout
conveyor
as follows. It places a copy of the first character that words
is attached to on the cout
conveyor and then places each succeeding character on the conveyor
until it
reaches the null character.

Figure 12.
Passing Arguments by
Reference
References are often easier to work with than pointers and in some
cases must
be used to achieve a program's objectives. In this section, I first
discuss
how references can be viewed in terms of the basic robot model, then I
show
how to use the model to explain the use of references in function
calls.
Reference Variables
In C++, a reference is an alias for an existing variable. Thus, the
following
declarations and assignment create a variable and a reference to that
variable.
int i = 7;
int& r = i; // r is a reference to i
The reference r is
simply another name for the variable i.
In this model, let's think of the reference
r as another label that the robot pastes onto the box labeled
i.
Assume the label is circular to distinguish it from the variable label
(Figure
13). Any statement using r
references the box labeled i
because the labels r
and i are on
the same
box.

Figure 13.
References and
Functions
To consider how passing arguments by reference works within this
model, consider
the following program, which uses a function to swap integer values by
using
call by reference.
//Program4.cpp
#include <iostream.h>
void RefSwap(int&, int&);
int main()
{
int i = 3,
j = 5;
cout << "Before RefSwap: i = " << i <<
" j = " << j << endl;
RefSwap(i, j);
cout << "After RefSwap: i = " << i <<
" j = " << j;
return 0;
}
void RefSwap(int& a, int& b)
{
int temp;
temp = a;
a = b;
b = temp;
}
The prototype for RefSwap()
tells CPU that the two arguments are to be passed by reference. This
means to
CPU that it must paste labels onto existing storage boxes when it gets
to the
RefSwap()
room. The
only way to do this is to carry the storage boxes themselves to the
function
room. Therefore, when the robot
calls RefSwap(i, j)
it carries the boxes i
and j
themselves to
the room
RefSwap().
The parameters in the function definition of RefSwap()
are declared as references. Hence, they are labels, not variables
(Figure 14).

Figure 14.
When the robot enters room RefSwap(),
it pastes the label a
(which is the name of its first parameter) on the first box it was
carrying.
Likewise, the robot pastes the second parameter label,
b, on the second box it was carrying (Figure 15).

Figure 15.
The first assignment the robot executes in RefSwap()
places the value that is in the box labeled a
into the variable temp.
Therefore, it actually places the value of the variable i
into temp.
Likewise
in the other statements, when the robot uses the boxes labeled a
and b, it is
really
using the boxes labeled i
and j (Figure
16).

Figure 16.
When the robot stops executing, it removes the labels it placed on
the boxes
(a and
b) and takes the boxes back to main().
The values in the boxes were interchanged in room RefSwap()
(Figure 17). Therefore, when CPU displays the values of i
and j for the
second
time in
main(), they
are the reverse of the values it displayed previously.

Figure 17.
Global and Static Variables
We can extend the basic robot model to include global variables
(declared outside
functions) and local static variables (local variables declared with
the keyword
static). A
global variable
is declared outside any function and, therefore, must exist outside
all function
rooms. Assume that MainStorage contains an area called StaticStorage.
When a
global variable is declared in a program, CPU places the variable in
StaticStorage
in such a way that only functions defined after the declaration have
access
to the variable. When in a function room, CPU can look out the
function rooms
window. The window has a shutter that opens only partway (Figure 18).
Thus,
the robot can only see into StaticStorage in one direction.

Figure 18.
Because static variables are in StaticStorage, their existence is
independent
of the activation/deactivation of any function room. Therefore, they
retain
their values independently of any function in the program.
A local static variable, that is one declared inside a function with
the keyword
static,
exists in the
StaticStorage area but is visible only to the function in which it is
declared
(Figure 19). Because the local static variable does not exist inside
the function
room, it retains its value from one function execution to the next.
Because
it is not in the room, CPU cannot return it to MainStorage when it
leaves the
room.

Figure 19.
We leave as an exercise for the reader to explain how the function
Func1()
in Program5.cpp works
in terms of the model. Note that when the robot sees a reference to a
variable
in an instruction, it first looks in the room in which it is executing
for a
variable of that name. If it finds such a variable, the robot uses it
to carry
out the instruction. If there is no variable of the given name in the
room,
the robot looks into StaticStorage for the variable.
Note that the scope resolution operator causes the robot to act
differently.
The reference to ::g3
in Func1()
causes CPU
to look into StaticStorage for the variable g3
rather than in the Func1()
room.
//Program5.cpp
#include <iostream.h>
void Func1();
int g1 = 6;
int main()
{
cout << "Global g1 = " << g1 << endl
<< endl;
Func1();
Func1();
return 0;
}
int g2 = 1;
int g3 = 7;
void Func1()
{
int g3 = 8;
static k = 2;
cout << endl << endl;
cout << "Function Func1() Executing" << endl
<< endl;
::g3++;
cout << "Global g1 = " << g1 << endl;
cout << "Global g2 = " << g2 << endl;
cout << "Global g3 = " << ::g3 << endl;
cout << "Local g3 = " << g3 << endl;
cout << "Local k = " << k << endl;
++g3;
++k;
cout << endl << "After incrementing, local
variables: " << endl;
cout << "Local g3 = " << g3 << "
Local k = " << k;
}
Acknowledgements
I would like to thank Professor Joe Bergin of Pace University for
several useful
comments that lead to improving the basic robot model. I would also
like to
thank Dean Susan Merritt, also of Pace University, for her continued
support
of this project.
Contributor
Dr. Molluzzo is chair of the Information Systems Department at Pace
University,
New York. He has authored articles in mathematics and various aspects
of computing.
He is also author of several books, including C++ for Business
Programming
(Prentice Hall, 1999). His main interests include computer
architecture, programming
languages, operating systems, and distance education. His main
interests outside
academe include the American Civil War, baseball, and bowling.
Contact
John C. Molluzzo
Information Systems Department
Pace University
Pace Plaza
New York, NY 10038
jmolluzzo@pace.edu
Copyright ©
2002, ISTE (International Society for Technology in Education). All
rights reserved.
|