UP ONE LEVEL: ENEL 315 Home Page

Memory Diagrams for C++, Part I
ENEL 315 Class Handout, Winter 1996

Author: Steve Norman
Date of first publication: 15 January 1996

Contents


Introduction

When you program in C++ it is important to be able to answer questions like, ``Where is the storage for the variable x?'' and ``When does the variable y disappear from the stack?''. Drawing memory diagrams can be very helpful in working out answers to such questions.

This handout contains memory diagrams for some common C++ situations. They use and extend conventions for C memory diagrams developed in the Fall 1995 version of ENGG 333. The ENGG 333 handouts Activation Records and the Stack and Static and Dynamic Allocation are very useful background reading.

If you did not take ENGG 333 in Fall 1995, see Dr. Norman to get paper copies of ENGG 333 handouts related to memory diagrams. You may also find it useful to use the World Wide Web to browse through ENGG 333 lab assignments and solutions.

[back to top of document]


Nested Scopes

First, some terminology:
A compilation unit is the C/C++ term for the text emitted by the preprocessor after it has finished its transformation of a source file. So a compilation unit typically consists of the contents of a few header files followed by declarations and function definitions from a .c or .cc file.

The scope of an identifier is the region within a compilation unit where an identifier is recognized.

In both C and C++ the scope of a global declaration is usually the region between the point of declaration and the end of the compilation unit, while the scope of a local declaration is usually the region between the point of declaration and the end of the innermost enclosing compound statement. The scope of a function argument usually extends from the point of declaration in the argument list to the end of the compund statement that forms the body of the function definition. (The complete rules are a lot more complex; see a C++ text for details.)

The following picture of a simple (and useless) C compilation unit (which is also a C++ compilation unit) shows examples of scopes:

The scopes of the two local variables and the function argument x are all nested within the scope of the global variable global. More interestingly, the scope of very_local is nested within the scope of local.

To program in C++ you must have a basic understanding of scope rules for local variables. Without such an understanding, you won't be able to determine which variables exist at any given point in time, nor will you be able to understand code that uses constructor and destructor functions.

The following ideas are important:

The complete rules for C++ are, alas, much more complicated, but these two ideas are a good starting point.

Now have a look at the program scope.cc. (Use the middle mouse button to pop up a second window with Mosaic or Netscape; or see the attached printout if you're reading a paper copy.) Here are memory diagrams for each of points one to four:

[back to top of document]


Reference Arguments

When working with C++, you should think of a reference as an alias (alternate name) for a data object. The data object to which a reference is attached is called the referent of the reference. A reference is not itself a data object; when a reference is used in a C++ expression it is the referent that is actually accessed.

(The previous paragraph probably created more confusion than it cleared up. Don't be discouraged; it will make more sense after you look at some examples and work with some code.)

Reference arguments to functions are the most straightforward and common of the several different ways to use references in C++. (We'll get to some of the other uses of references later in the course.) Reference arguments are essentially the same as what are called variable parameters in Pascal; they also work pretty much the same way as Fortran subroutine arguments.

The program swaps.cc shows a classic use of reference arguments - swapping the values of two variables. (Flip forward a few pages if you're reading a paper version of this handout.) The program also shows how using value arguments will not work, and how pointer arguments could be used to solve the problem.

Here is a memory diagram showing three instants in the execution of the program:

The diagram is intended to emphasize an important point: when you work with C++ code, references are NOT the same as pointers. (On the other hand, at the machine-code level, pointers and references often do the same thing: communicate the memory address of a data object.) The pointers p1 and p2 are distinct data objects from the variables c and d; you could make them point at other ints if you wanted to, although of course that wouldn't help you to swap the contents of c and d. On the other hand the references r1 and r2 are not data objects; they are merely aliases for the variables e and f.

By the way, while the dot-and-arrow notation for pointers is very widely used in programming textbooks, the dotted-line-and-circle notation I've used to illustrate references is something I made up myself. (Making up a graphical notation for references is hardly an original idea; I just want to warn you that you aren't likely to see my particular notation outside of ENEL 315 handouts and lecture notes.)

Is there such a thing as a reference to a reference? No, there is not. Instead, if reference ref1 is used to initialize reference ref2, ref2 is established as a new alias for the referent of ref1.

Confused yet? An example should help. In the program refref.cc, a reference argument, ref1, is used to initialize a second reference argument, ref2. The diagram below shows clearly that ref2 is set up as an alias for the same int that ref1 is an alias for.

[back to top of document]


Class Member Functions

Calling a class member function (sometimes called a method) generates an activation record in much the same way that calling a ``regular, C-like'' function would. There is space in the activation record for function arguments and space for local variables. However, in addition to accessing arguments and local variables, member functions also access member variables of the object with which the function was called. How does a member function know which object it is supposed to act on?

Read through the program classmem.cc. (Again, a printout is supplied with paper copies of this handout.) The output is

   value of i from thing_one is 47
   value of i from thing_two is 5
How does the function Thing::add know that it should modify the i belonging to thing_one and not the i belonging to thing_two? The answer is shown in the diagram below.

In addition to its explicit arguments (the arguments specified by the programmer) there is one more argument that is implicit (generated by the compiler without the programmer asking for it). This extra, implicit argument is called this. That's right, this is a word with special meaning in C++. The this pointer points to the object with which the function was called. So in the example, the simple expression i and the slightly more complicated expression this->i have identical meanings within the definitions of the member functions of class Thing.

[back to top of document]