TOC PREV NEXT INDEX


I've just released a deduplicating backup product for
VMWare Workstation and Server.
Do you need to reduce the storage needed to maintain multiple backups?
Or do you need multiple snapshots on VMWare Server?
If you have either of these needs, just click
here to get started for only $29 (limited time offer ends July 31st, 2011)!

C++: A Dialog


CHAPTER 5 Functional Literacy
C++ was intended to be useful in writing large programs. Such programs are usually composed of many implementation files, as I mentioned in Chapter 3.

In such a case, we must have some way of creating an executable program (sometimes abbreviated to just an executable) from a number of implementation files. We also need some way for code in one module to refer to code in another one. Similarly, we have to be able to specify where execution of our program should start; this is taken care of by the C++ rule that execution always starts at the block called main.

As we've already seen, the computer can't execute source code. Therefore, any implementation files we write have to be translated into object code; the result of such translation is an object code module. One other kind of module we're interested in is the library module, which contains the object code from several implementation files.

The idea of various types of modules led to the following discussion with Susan:

Susan: So an object file is like some kind of interface between your code and the binary numbers of the machine? I am confused.
Steve: Each implementation file is translated by the compiler into one object file. Then these object files will be combined along with some previously prepared library files to make an executable program.
Susan: iostream is a library? So are these already written programs that you can refer to like a real library?
Steve: Yes. Actually, iostream is now part of the C++ standard library; that happened a few years ago when they standardized the library.
Susan: The libraries contain code segments that are generalized, and the other modules contain code segments that are program specific?
Steve: Right. One point that should be emphasized, though, is that a library contains object code, not source code.
Susan: Where is the library? I am serious.
Steve: You can have more than one library, including ones written for more specialized purposes by different companies. The library files for the compiler on the CD in the back of the book are kept in a subdirectory under the directory where the compiler is installed.
Susan: So what is an "implementation file"? Is it a set of code written by the programmer?
Steve: It's a file containing part of a program, in source code. Most significant programs consist of a number of modules (files), rather than one big module (file), partly because it's easier to find and edit one logical segment of a program in a separate file than a whole bunch of code all in one big file.
Susan: Okay then, so a module is just a big logical segment? How is it delineated from the rest of the program? Is it named? How do you find it? Can you just tell by looking where a module starts and ends?
Steve: A module, in C++ terminology, is another name for a file. Therefore, an implementation module is an implementation file, which contains program statements. It has a name, which is the name of the file.
Susan: So an implementation file is like a library, only as we have discussed it is more specific than a library; it is for the program that you are working on?
Steve: Right.
Susan: Where are these modules and how do they get there?
Steve: Wherever you (or whoever wrote the code) put them. In the case of your "weight-writing" program, the code you wrote is in an implementation file. That module is compiled to make an object code module, which is then combined with library module(s) that come with the compiler to make an executable file that can be run.
Susan: So then the implementation file is a "miniprogram" within a program that holds the source code to be later compiled?
Steve: It contains part of the source code of a program, which needs to be compiled and combined with other previously compiled code before it can be used. I think that's the same as what you're saying.
Actually, I've misused C++ terminology a little here in the interests of comprehensibility. The term block isn't quite correct as applied to main(); the correct term is function. Let's take a look at the difference between these two concepts, some other related definitions, and the objectives of this chapter.
5.1. Definitions
An implementation file is a file that contains source code for a program. Almost every part of every program starts out as an implementation file.
Compilation of an implementation file produces a file called an object code module (or object file), which contains object (machine) code.
Several object code modules of a generally useful nature can be combined to make a file called a library module, usually abbreviated to library.
A function is a section of code somewhat like a block, but with somewhat different characteristics. For one thing, you can't substitute a function for a statement; also, a function has a name, whereas blocks are anonymous. This name enables one function to start execution of another one.
A function call (or just "call" for short) causes execution to be transferred temporarily from the current function to the one named in the call.
A called function is a function that starts execution as a result of a function call.
A calling function is a function that suspends execution as a result of a function call.
A return statement is the mechanism used by a called function to return to the calling function, which picks up just where it left off.
A console mode program is a program that looks like a DOS program rather than a Windows program.

5.2. Objectives of This Chapter

By the end of this chapter, you should
1. Understand how and when to use functions to reduce the amount of code you have to write.
2. Understand what software really is.
3. Understand how your source code is turned into an executable program.
4. Understand how storage is assigned to different types of variables.
5. Understand how functions can call one another.

5.3. Modules vs. Functions

Susan had a question about this new notion of a function, as related to modules:
Susan: So a module has nothing to do with blocks and functions? If a function only "calls" another function, then how do you call a module?
Steve: You can't call a module. In fact, although a few language features apply to modules rather than functions, modules don't really have much significance in C++ other than as places to store related functions.
When we call a function, we usually have to provide it with input (for example, some values to be averaged) and it usually produces output which we use in further processing (for example, the average of the input values). Some functions, though, have only one or the other. For example, some functions are organized in pairs consisting of one storage function and one retrieval function; the first stores data for the second to retrieve later. In that case, the storage function may not give us anything back when we call it, and the retrieving function may not need any input from us.

To see how and why we might use a function, let's take a look at a program having some duplicated code (Figure 5.1).

FIGURE 5.1. A sample program with duplicated code (code\nofunc.cpp)
#include <iostream>
using namespace std;

int main()
{
short FirstWeight;
short SecondWeight;
short FirstAge;
short SecondAge;
short AverageWeight;
short AverageAge;

cout << "Please type in the first weight: ";
cin >> FirstWeight;

cout << "Please type in the second weight: ";
cin >> SecondWeight;

AverageWeight = (FirstWeight + SecondWeight) / 2;

cout << "Please type in the first age: ";
cin >> FirstAge;

cout << "Please type in the second age: ";
cin >> SecondAge;

AverageAge = (FirstAge + SecondAge) / 2;

cout << "The average weight was: " << AverageWeight << endl;
cout << "The average age was: " << AverageAge << endl;

return 0;
}

I'd like you to look particularly at this line:

AverageWeight = (FirstWeight + SecondWeight) / 2;

and this one:

AverageAge = (FirstAge + SecondAge) / 2;

These two lines are awfully similar; the only difference between them is that one of them averages two weights and the other averages two ages. While this particular example doesn't take too much code to duplicate, it may not be difficult for you to imagine the inefficiency and nuisance of having to copy and edit many lines of code every time we want to do exactly the same thing with different data. Instead of copying the code and editing it to change the name of the variables, we can write a function that averages whatever data we give it.

Figure 5.2 is a picture of a function call. The calling function (1) is main; the function call is at position (2). The called function is Average (3), and the return is at position (4); the returned value is stored in the variable AvgAge, as indicated by the assignment operator = in the statement

AvgAge = Average(FirstAge,SecondAge);

and the calling function, main, resumes execution at line (5).
FIGURE 5.2. A function call
By the way, it's important to distinguish between returning a value from a function, which is optional, and returning control from a called function to its calling function, which always happens at the end of the called function (unless the program has terminated due to an error in the called function).1

While we're on the subject of the calling function, you may be wondering why we started the example at the beginning of main. That's because every C++ program starts executing at that point. When the main function calls another function, such as Average, then main is suspended until Average is finished. When Average finishes, main resumes where it left off.

This isn't limited to one "level" of calls. The same thing can happen if Average (for example) calls another function, let's say Funcx; Average will wait until Funcx returns before continuing. Then when Average finishes, it will return to main, which will take up where it left off.

This idea of calling and returning from functions led to the following discussion with Susan:

Susan: So if you wanted to be really mean you could get into someone's work in progress and stick a return somewhere in the middle of it and it would end the program right there? Now that I am thinking about it, I am sure you could do a whole lot worse than that. Of course, I would never do such a thing, but what I am saying is that whatever you are doing when the program gets to the return statement, then it is the end? Next stop, C:\?
Steve: Yes and no. If you're in main, then a return statement means the program is finished and if it is a console mode program like the ones we are writing here, you will indeed see the command line prompt come up on your screen. If you're in a function other than main, it means "return to the function that called this function". In the case of a function that returns a value, the expression in the return statement tells the compiler what value to use in place of the function call. For example, the statement AvgAge = Average(i,j); sets AvgAge to the result in the return statement of the function Average. As you can see by looking at that function, the returned value is the average of the two input values, so that is the value that AvgAge is set to by this statement.
Susan: OK, but what about the return 0; at the end of the main program? Why should it be 0?
Steve: The return statement in main can specify a different value if you wish. However, the custom is to return 0 from main to mean "everything went well" and some value greater than 0 to mean "there's a problem". This isn't entirely arbitrary, because a batch file can test that return value and use it to alter the flow of the execution of commands in the batch file.
Susan: OK, let's see if I have this right: The return statement has to match the main statement. This is so confusing. Look, when you say "The value that is returned, 0, is an acceptable value of the type we declared in the line int main ()" - since I see no 0 anywhere around int main () - you are referring to the int. An int can have a value of 0, right?
Steve: Right, the 0 has to match the int. That's because a function can have a return type, just like the type of a variable. In this case, int is the type of the main function and the value is filled in by the return statement.
Susan: OK, then all this is saying is that the value that is produced is the same type as that declared at the beginning of a program. Since we declared the type of main as an int, if the value produced were a letter or a picture of a cow, then you would get an error message?
Steve: Well, actually a letter (i.e., a char) would be acceptable as an int, due to rules left over from C. Otherwise, you're exactly correct.
Susan: Hey, where is the 0 coming from to be returned?
Steve: It's specified as a literal value in the return statement; you could put any legal int value in there instead, if you wanted to.
Susan: So the return value doesn't have to be a 0?
Steve: Right.
Susan: So 0 could be another int value, but it can't be a variable? Even I don't know what I am talking about now!
Steve: I think I've confused you unnecessarily. You can certainly return a value that is specified as a variable, such as return i;. What I meant was that the 0 we're returning in this case is a constant, not a variable.
Susan: The picture helps with the calling confusion. But I don't understand why main is the calling function if the calling function suspends execution. How can you initiate a function if it starts out suspended? But I am serious.
Steve: The main function starts execution as the first function in your program. Therefore, it isn't suspended unless and until it calls another function.

An Example of Using a Function

I think it's time for a more detailed example of how we would use a function. Suppose we want to average several sets of two numbers each and we don't want to write the averaging code more than once. The Average function just illustrated provides this service; its input is the two numbers we want to average and its output is the average. Figure 5.3 shows the code for the function Average without all the lines and arrows:
FIGURE 5.3. A function to average two values

short Average(short First, short Second)

{

short Result;

Result = (First + Second) / 2;

return Result;

}

You can try out this function in a running program in the usual way. The name of the program is func1.cpp and you can see it in Figure 5.5 on page 239.2 As had become routine, I couldn't sneak this idea (of writing a function) past Susan without a discussion.
Susan: Where you say "and we don't want to write the averaging code more than once", are you just saying if you didn't do the Average function thing then you would have to write this program twice? I mean for example would you have to write a program separately for weights and then another one from the beginning for ages?
Steve: We wouldn't have to write an entirely separate program; however, we would have to write the averaging code twice. One of the main purposes for writing a function is so that we don't have to repeat code.
To analyze this piece of code, let's start at the beginning. Every function starts with a function declaration, which tells the compiler some vital statistics of the function. The function declaration consists of three parts:
1. A return type;
2. The function's name;
3. An argument list.
In the case of our Average function, the function declaration is short Average(short First, short Second). The return type is short, the name of the function is Average, and the argument list is (short First, short Second). Let's take these one at a time.

Returning Data to the Calling Function

The first part of the function declaration is the return type, in this case short. This indicates that the function Average will provide a value of type short to the calling function when the Average function returns. Looking at the end of the function, you will see a statement that says return Result;. Checking back to the variable definition part of the function, we see that Result is indeed a short, so the value we're returning is of the correct type. If that were not the case, the compiler would tell us that we had a discrepancy between the declared return type of our function and the type actually returned in the code. This is another example where the compiler helps us out with static type checking, as mentioned in Chapter 3; if we say we want to return a short and then return some other incompatible type such as a string, we've made a mistake.3 It's much easier for the compiler to catch this and warn us than it is for us to locate the error ourselves when the program doesn't work correctly.

Susan wanted to know more about the return type. Here's the conversation that ensued:

Susan: This return type thing - it will have to be the same type of value as the output is?
Steve: For our purposes here, the answer is yes. As I've already mentioned, there are exceptions to this rule but we won't need to worry about them.
Susan: Do you always use the word return when you write a function?
Steve: Yes, except that some functions have no return value. Such functions don't have to have an explicit return statement, but can just "fall off the end" of the function, which acts like a return; statement. This is considered poor form, though; it's better to have a return statement.
The function name (in this case, Average) follows the same rules as a variable name. This is not a coincidence, because both function names and variable names are identifiers, which is a fancy word for "user-defined names". The rules for constructing an identifier are pretty simple, as specified by the C++ Standard:4 "An identifier is an arbitrarily long sequence of letters and digits. ... Upper- and lower-case letters are different. All characters are significant." (p. 14)
In other words:
1. Your identifiers can be as long as you wish. The compiler is required to distinguish between two identifiers, no matter how many identical characters they contain, as long as at least one character is different in the two names.5
2. They can be made of any combination of letters and digits, as long as the first character is a letter.6
3. The upper and lower case versions of the same character aren't considered equal as far as names are concerned; that is, the variable xyz is a different variable from Xyz, while XYZ is yet another variable. Of course, you may get confused by having three variables with those names, but the compiler considers them all distinct.
By the way, the reason that the first character of an identifier can't be a digit is to make it easier for the compiler to figure out what's a number and what isn't. Another rule is that identifiers cannot conflict with names defined by the C++ language (keywords); some examples of keywords that we've already seen are if and short.

Finally, we have the argument list. In this case, it contains two arguments, a short called First, which holds the first number that our Average function uses to calculate its result; and a second argument (also a short) called Second, which of course is the other number needed to calculate the average. In other cases, there might be several entries in the argument list, each of which provides some information to the called function. But what exactly is an argument?

Function Arguments

The question of what is an argument is more subtle than it may appear. An argument is a value that is supplied by a function (the calling function) that wishes to use the services of another function (the called function). For example, the calling function might be our main function, and the called function might be our Average function, while the arguments are two short values to be averaged. Arguments like the ones here are actually copies of values from the calling function; that is, the compiler will initialize the variable named in the argument list of the called function to the value supplied by the calling function. This process of making a copy of the calling function's argument is referred to as call by value, and the resulting copy is called a value argument.7 Figure 5.4 is an example of this argument passing mechanism at work with only one argument.
FIGURE 5.4. Argument passing with one argument (code\birthday.cpp)
#include <iostream>
using namespace std;

short Birthday(short age)
{
age ++;
return age;
}

int main()
{
short x;
short y;

x = 46;
y = Birthday(x);

cout << "Your age was: " << x << endl;
cout << "Happy Birthday: your age is now " << y << endl;

return 0;
}

In this program, main sets x to 46 and then calls Birthday with x as the argument. When Birthday starts, a new variable called age is created and set to 46, because that's the value of x, the argument with which main called Birthday. Birthday adds one to its variable age, and then returns the new value of that variable to main. What number will be printed for the variable x by the line cout << "Your age was: " << x << endl;? Answer: 46, because the variable age in Birthday was a copy of the argument from main, not the actual variable x named in the call to Birthday. On the other hand, the value of y in the main program will be 47, because that is the return value from Birthday.

As you might have guessed, the notion of copying the argument when a function is called occasioned an intense conversation with Susan.

Susan: This is tough. I don't get it at all. Does this mean the value of the short named x will then be copied to another location in the function named Birthday?
Steve: Yes, the value in the short named x will be copied to another short called age before the execution of the first line in the function Birthday. This means that the original value of x in main won't be affected by anything that Birthday does.
Susan: Now for the really confusing part. I don't understand where you say "An argument like the one here (short age) is actually a copy of a value in the calling function". Now, I have read this over and over and nothing helped. I thought I understood it for a second or two and then I would lose it; finally I have decided that there is very little in this section that I do understand. Help.
Steve: When you write a function, the normal behavior of the compiler is to insert code at the beginning of the function to make a copy of the data that the calling function supplies. This copy of the data is what the called function actually refers to, not the original. Therefore, if you change the value of an argument, it doesn't do anything to the original data in the calling function.
If you (the programmer of the function) actually want to refer to the data in the calling function and not a copy of it, you can specify this when you write the function. There are cases in which this makes sense and we'll see some of them in Chapter 6.
Susan: I don't understand why it is a copy of the calling function and not the called function.
Steve: It's not a copy of the calling function; it's a copy of the value from the calling function, for the use of the called function. In the sample program, main sets x to 46 and then calls Birthday with x as the argument. When Birthday starts, a new variable called age is created, and set to 46, because that's the value of x, the argument with which main called Birthday. Birthday adds 1 to its variable age, and returns the new value of age to main. What will be printed by the line "cout << x << endl;"? Answer: 46, because the variable age in Birthday was a copy of the value of the argument from main, not the actual variable (x) specified in the call to Birthday. Does this explanation clarify this point?
Susan: I still don't understand the program. It doesn't make any sense. If x = 46, then it will always be 46 no matter what is going on in the called function. So why call a function? You know what, I think my biggest problem is that I don't understand the argument list. I think that is where I am hung up on this.
Steve: The arguments to the function call (x, in the case of the function call Birthday(x)) are transferred to the value of the argument in the function itself (the short variable age, in the case of the function Birthday(short age)).
Susan: In that case, why bother putting an x there, why just not put 46? Would it not do the same thing in the called function, since it is already set to 46?
Steve: Yes, but what if you wanted to call this function from another place where the value was 97 rather than 46? The reason that the argument is a variable is so you can use whatever value you want.
Susan: If we called Birthday with the value 46, then the 46 would be 46++, right?
Steve: 46++ is a syntax error, because you can't change the value of a literal constant. Only a variable can be modified.
Susan: So if you want to state a literal value, do you always have to declare a variable first and then set a variable to that literal value?
Steve: No, sometimes you can use a literal value directly without storing it in a variable. For example,

cout << 15;

or

cout << "Hello, my name is Steve Heller";

What I was trying to say is that you can't change a literal value. Thus, 15++; is not legal because a literal value such as 15 represents itself, that is, the value 15. If you could write 15++;, what should it do? Change all occurrences of 15 to 16 in the program?
Susan: Okay. Now, how does age get initialized to the value of x?
Steve: The compiler does that when it starts the function, because you have declared in the function declaration that the argument to the function is called age, and you have called the function with an argument called x. So the compiler copies the value from x into age right before it starts executing the function.
Susan: Oh, I see. That makes sense, because maybe later on you want to call the same function again and change only a little part of it, but you still need the original to be the same, so you can just change the copy instead of the original. Is that the purpose?
Steve: Not quite. The reason that the called function gets a copy of data, rather than the original, is so that the person writing the calling function knows that the original variable hasn't been changed by calling a function. This makes it easier to create programs by combining your own functions with functions that have already been written (such as in the library). Is that what you meant?
Susan: So is everything copied? I am getting confused again, are you going to talk a little more about copying in the book? Have I just not gotten there? Anyway, if you haven't mentioned this more, I think you should, it explains hidden stuff.
Steve: Don't worry, we're going to go into much more detail about how this works. In fact, it's a major topic in the rest of the book.

How the Average Function Works

The same analysis that we have just applied to the Birthday function applies also to the Average function that we started out with; the arguments First and Second are copies of the values specified in the call to Average.

Now that we have accounted for the Average function's input and output, we can examine how it does its work. First, we have a variable definition for Result, which will hold the value we will return to the calling function; namely, the average of the two input values.

Then we calculate that average, with the statement

Result = (First + Second) / 2;

Once the average has been calculated, we're ready to return it to the calling program which is accomplished by the line return Result;. Finally, we reach the closing }, which tells the compiler that the function is done.

5.4. Using a Function

Now that we have seen how to write the Average function, let's see how to use it to solve our original problem. The program in Figure 5.5 uses our Average function twice, once to average two weights and once to average two ages.
FIGURE 5.5. Using the Average function (code\func1.cpp)
#include <iostream>
using namespace std;

short Average(short First, short Second)
{
short Result;

Result = (First + Second) / 2;

return Result;
}

int main()
{
short FirstWeight;
short SecondWeight;
short FirstAge;
short SecondAge;
short AverageWeight;
short AverageAge;

cout << "Please type in the first weight: ";
cin >> FirstWeight;

cout << "Please type in the second weight: ";
cin >> SecondWeight;

AverageWeight = Average(FirstWeight, SecondWeight);

cout << "Please type in the first age: ";
cin >> FirstAge;

cout << "Please type in the second age: ";
cin >> SecondAge;

AverageAge = Average(FirstAge, SecondAge);

cout << "The average weight was: " << AverageWeight << endl;
cout << "The average age was: " << AverageAge << endl;

return 0;
}
As always, calling a function requires specifying its name and its argument(s) and doing something with the return value, if any. In this case, we call Average with the arguments FirstWeight and SecondWeight, and store the result in AverageWeight. This is accomplished via the line AverageWeight = Average(FirstWeight, SecondWeight);. Later, we call Average with the arguments FirstAge and SecondAge, and store the result in AverageAge. We do this via the line AverageAge = Average(FirstAge, SecondAge);.

The value of writing a function to average two numbers wasn't obvious to Susan at first. After some discussion, however, she agreed that it was valuable. Here's the conversation that convinced her:

Susan: In general, I just don't understand why you even need to call the Average function in the first place; it looks like extra steps to me. It seems to me that all you need are your two input values, which end up just giving you the results right there for weight and age. I think that this is what bothers me the most. For example, when you get done with the set of weights, you should just have your results right then and there instead of calling the function of Average.
Steve: But what is the result you want? You want the average of the weights. Where is that calculated?
Susan: After you are done with that, then you already have written a set of ages so you can just use the result of that. It just seems like you are going in circles unnecessarily with this program. That is why I don't understand it.
Steve: Again, just because you have a set of ages doesn't mean that you have the average age; some code somewhere has to calculate that average.
Susan still had a lot of trouble with visualizing the way this function worked. However, running it in the debugger got her moving again, resulting in the following discussion:
Susan: Why does everything start out initialized to 0 except Result, which appears to hold an address in memory?
Steve: The values of uninitialized variables are not reliable. In this case, I'm getting a similar value of Result to the one you're getting; however, you cannot count on this. There's also no reason to think that the contents of Result are a memory address; they're just garbage until the variable is initialized.
Susan: Steve, I don't understand this; first you tell me that those numbers are garbage but represent addresses in memory and now you tell me that they are just garbage, but that they are not reliable. I don't understand, if they are uninitialized, how they ever could be reliable. This implies that at some time you could get an expected value even if they are uninitialized. They should always be garbage. So, when do those numbers represent memory addresses and when not?
Steve: Apparently I've confused you unnecessarily again. Here are the facts:
1. A variable always represents an address in memory.
2. However, the contents of an uninitialized variable are garbage.
3. Since they are garbage, they represent nothing.
4. Since they are garbage, they can have any value, which may or may not appear to have meaning. Regardless of appearances, the value of an uninitialized variable is meaningless.
Then our discussion returned to the topic of how the main program works:
Susan: Oh, OK, so AverageWeight = Average(FirstWeight, SecondWeight); is the part that starts the Average function running?
Steve: Right.
Susan: Then after averaging the weights, why does Result go to 0? It looks to me that Result has no value at these points and I don't understand why.
Steve: Because you're looking at the next call to Average, where its variable Result is uninitialized again. By default, variables are uninitialized whenever they are created, which occurs each time the function where they "live" is entered. The "old" Result from the first call to Average "died" when the first call to Average was finished, and the new Result that is created on the second call is uninitialized until we set it to some known value.
The next topic we discussed was how to create a new program and get it to run.
Susan: Now when you start out a new program; are all the new implementation files named with a .cpp extension?
Steve: Yes.
Susan: So this code in Average is where the real averaging takes place, right? Is this the "Average command"? I thought Average meant to average, so what is the deal?
Steve: The deal is that something has to do the averaging; rather than writing the same code every time we need to average another set of two numbers, we put that code in one place (the Average function) and call it whenever we need its assistance.
Susan: OK, then this brings up another one of my questions. How come you write the Average function before the main function?
Steve: So that the main function knows how to call the Average function. There's another way to allow a function to call another one that doesn't come before it in the file, but I thought it was easier to show it this way at first.
Susan: If the main function is going to be executed first, then how come the Average function is written first? Does the compiler always look for main first?
Steve: Yes.
Susan: Yeah, but could the Average function then just be written after main instead of before it? Just to be there when it is needed, instead of before it is needed? Am I right that you still would not have to write it twice; it would still be there for the next time it is needed, right?
Steve: The Average function can be anywhere, even in a different module, but at the point that you try to use it, the compiler has to know about it. To be precise, you have to specify its name, return type, and what arguments it takes. Otherwise, the program won't compile. On the other hand, the compiled version of the Average function doesn't have to be available until the executable is created;8 if it's not available at that point, you'll get an error saying that you have referenced an undefined function.
Susan: So does that mean you could put the Average function anywhere you want? Then could it or any "subfunction" be put anywhere you want because the main function would always be executed first? Or could you mess up the code if you put it in a really ridiculous place like inside an output or input statement. . . or could the compiler be able to ignore something like that and go about business as usual? I guess because of the brackets it should ignore such a thing but I am not sure. See, these are the things that we novices are obliged to ponder.
Steve: You can't start a function definition in the middle of another function. That's called a nested function and it's not allowed in C++. The rule can be stated approximately as "You can start a function definition anywhere except in the middle of another definition."
Susan: So then the "cue" for the Average function to begin is the word Average (weight) or (age), when the compiler sees that word it just begins that separate function to start its little calculation.
Steve: That's right, except that it needs two arguments, not just one.
Susan: And that function since it was named Average causes the averaging function to work. Is that how it goes?
Steve: If I understand your question, it's not the name that makes the Average function do the averaging, it's the code that adds up the two values and divides by 2. We could replace all the references to Average with the word Glorp and the compiler wouldn't care; however, a future programmer trying to read the program probably wouldn't be amused by that name.
Susan: Oh, so there is nothing magical about the word Average, I thought it might trigger a function of averaging. Well, that sounds reasonable; it's more for us humans than the computer. And then that brings up another question, along the same line of thinking. After the Average function has done its thing, how does the program go from return Result; to the next output statement that asks for the ages? What triggers that change? I am not seeing this in the code.
Steve: The return keyword tells the compiler to hand back control to the function that called the one where the return is, as indicated in Figure 5.2.
This discussion didn't slake her thirst for knowledge about how to write a program. Here is how we continued:
Susan: Can I mix shorts with strings using the headers that are already stated in the test program?
Steve: Mixing shorts with strings is a dubious proposition, sort of like adding apples and oranges together; could you be more specific about what you're trying to do?
Susan: What if you wanted to add a numerical value to your program such as test: You have to put in a short, right? So if you added a short, what else would you have to do to make it work? Or would you have to start over with another main function after the first part and then declare new variables? I tried that too, and the compiler did not like that either. Very inflexible it is. I will tell you after one more try what I am doing. This will keep you in suspense.
Steve: It depends on what you're trying to do with the short. It's usually best to have a specific problem in mind that you're trying to solve by writing a program. Then you may see how to use these facilities (shorts, strings, Vecs, etc.) to solve your problem; if not, you can ask me how they would fit into the solution.
As for your second suggestion, you're not allowed to have more than one main function in a program, because the compiler wouldn't know which one to use as the starting address for the program.
Susan: I am not really trying to solve anything, I just want to have the user type in more info and that info is a number - wait!! That is it, in that case it will be like an ASCII character and it doesn't need a short, right? That's right. I can still use a string. We are not crunching any numbers with this.
Steve: As long as you don't try to do any calculations, you can read the data into a string, even data that looks like a number; of course, that data entry method is pretty loose, since if the user types "abc" as an age, the program will accept it.
Susan: Can you define a string without a word but with just a wildcard type of variable like when we use i in shorts? In other words, does it matter what we call a variable?
Steve: A variable is always a "wildcard", whether it's a short or a string. For example, a short variable always has a name, such as i, or index (or whatever makes sense to you), and a value, which is a number such as 14 or 99. A string variable also has a name, such as FirstName, or street (or whatever makes sense to you), and a value, which consists of characters rather than a number, such as "Susan" or "Wesley".
As you can see, using a function isn't very difficult. We have to provide it with the material to work on (its arguments) and can store its return value in a variable for further processing (or use it directly, if we wish). But there's a little more here than meets the eye. How does the variable FirstWeight, for example, get transformed into the variable First that we used when writing the function?

This explanation requires us to look at some more underlying software technology. To be precise, we're going to spend some time examining the infrastructure that makes computers usable for programmers. First, though, we have to consider a more general notion, that of a "virtual computer".

5.5. Software Is a Virtual Computer

Unlike many words in the vocabulary of computing, virtual has more or less retained its standard English definition: "That is so in essence or effect, although not formally or actually; admitting of being called by the name so far as the effect or result is concerned."9 In other words, a virtual computer would be something that acts just like a computer, but really isn't one. Who would want such a thing?

Apparently everyone, since virtual computer is just another name for what we have been calling software. This may seem a rash statement, but it really isn't. One of the most important mathematical discoveries (inventions?) of the twentieth century was Alan Turing's demonstration in 1936 that it was possible to create a fairly simple computing device (called a Turing machine for some obscure reason) that could imitate any other computing device. This machine works in the following way: You provide it with a description of the other computer you want it to imitate and it follows those directions. Suppose we want a computer that calculates only trigonometric functions. We could theoretically write a set of instructions as to how such a computer would behave, feed it into a Turing machine and have the Turing machine imitate the behavior of this theoretical "trigonometric computer".

Susan was quite impressed by this achievement:

Susan: Was this Turing guy some kind of genius?
Steve: Yes.
This is undoubtedly interesting, but you may be wondering what it has to do with programming. Well, what do we do when we write a program? In the case of our pumpkin weighing program, we're describing the actions that would be taken by a hypothetical "pumpkin weighing computer". When we run the program, the real computer simulates these actions. In other words, we have created a virtual pumpkin weighing computer.

The same analysis applies to any program. A program can most fundamentally be defined as instructions to a "universal computer", telling it how to simulate the specialized computer you actually want to use. When the universal computer executes these instructions, it behaves exactly as the hypothetical specialized computer would.

Of course, actual computers aren't really universal; they have limits in the amount of memory or disk space they contain and the speed of their execution. However, for problems that can be solved within those limits, they are truly universal computing devices that can be tailored to a particular problem by programming.

Object Files

Now let's take a look at one of these areas of software technology. We've already seen that the function of a compiler is to convert our human-readable C++ program into machine instructions that can be executed by the computer. However, the compiler doesn't actually produce an executable program that can stand by itself; instead, it translates each implementation file into a machine language file called an object code module (or object file). This file contains the instructions that correspond to the source code statements you've written, but not the "infrastructure" needed to allow them to be executed. We'll see what that infrastructure does for us shortly.

The creation of an object file rather than a complete executable program isn't a universal characteristic of compilers, dictated by nature. In fact, one of the most popular compilers in the early history of the PC, the Turbo Pascal compiler, did create an executable file directly from source code. This appears much simpler, so we have to ask why this approach has been abandoned with C++. As I've mentioned before, C++ was intended to be useful in writing large programs. Such programs can consist of hundreds or even thousands of modules (sections of source code) each containing hundreds or thousands of lines of code. Once all the modules are compiled, the object files resulting from the compilation are run through a program called the linker. The linker combines information from all of the object files, along with some previously prepared files called library modules (or libraries), to produce an executable program; this is called linking the program. One reason for this two-step approach is that we wouldn't want to have to recompile every module in a large program every time we made a change in one section; therefore, only those modules that have been affected are recompiled.10 When all of the affected modules have been recompiled, the program is relinked to produce an updated executable.

To make such a system work, it's necessary to set up conventions as to which parts will be executed first, where data needed by more than one module will be stored, and so on. Also, a lot of operations aren't supplied as part of the language itself but are very handy in writing programs, such as the I/O functions that we've already seen. These make up the infrastructure needed to execute C++ programs.

Figure 5.6 is a picture of the process of turning source code into an executable file.

Susan found this explanation and diagram to be helpful.

Susan: This is beginning to come into focus. So you write your source code, it has a middle man called an object file and that just passes the buck over to a linker, which gathers info the program may need from the libraries, and then the program is ready to be read by the machine. Close?
Steve: Yes, very close indeed.
FIGURE 5.6. Making an executable

Operating System Facilities

As is often the case in programming, this infrastructure is divided into several layers, the higher ones depending on the lower ones for more fundamental services. The lowest level of the infrastructure is supplied by the operating system, a program that deals with the actual hardware of your computer. By far the most common operating system for Intel® CPUs, as this is written, is some variant of Microsoft Windows, with Linux® in a distant second place. All of these provide some of the same facilities; for example, you are accustomed to dealing with files and directories when using application programs such as word processors and spreadsheets. However, the disk drive in your computer doesn't know anything about files or directories. As we have seen in Chapter 2, all it can do is store and retrieve fixed-size pieces of data called sectors, given an absolute address on the disk described by a platter, track number, and sector number. Files are a creation of the operating system, which keeps track of which parts of which files are stored where on the disk.11

A modern operating system provides many more facilities than just keeping track of file storage. For example, it arranges for code and data to be stored in separate areas of RAM with different access rights, so that code can't be accidentally overwritten by a runaway program; that is, one that writes outside the memory areas it is supposed to use. This is a valuable service, as errors of this kind are quite difficult to find and can cause havoc when they occur.

That's the good news. The bad news is that MS-DOS, which is still the basis of all versions of Windows before Windows NT® and Windows 2000®, was created before the widespread availability of reasonably priced CPUs with memory protection facilities. For this reason, when using those earlier operating systems, it's entirely possible for a runaway program to destroy anything else in memory. Theoretically, we should all be running "real" operating systems by the time you read this; so far, though, the rumors of the demise of MS-DOS have been greatly exaggerated.

This notion intrigued Susan. Here's how that conversation went:

Susan: What is a runaway program?
Steve: One that is writing in areas that it shouldn't, thus destroying data or programs outside its assigned memory areas.
Susan: How would an operating system actually separate code from data areas? Would it be a physical thing?
Steve: What makes this possible are certain hardware mechanisms built into all modern CPUs, so that certain areas of memory can be assigned to specific programs for use in predefined ways. When these mechanisms are used, a program can't write (or read, in some cases) outside its assigned area. This prevents one program from interfering with another.

Library Modules

The next level of the infrastructure is supplied by the aforementioned library modules, which contain standardized segments of code that can perform I/O, mathematical functions, and other commonly used operations. So far, we have used the iostream and string parts of the C++ standard library, which provided the keyboard input and screen output in our example programs.12 We've also relied implicitly on the "startup" library, which sets up the conditions necessary for any C++ program to execute properly.

Susan wanted to know how we were using the startup library:

Susan: I don't know what you are talking about when you say that we have also used a startup library. When did we do that? At startup? Well, is it something that you are actually using without knowing you are using it?
Steve: Yes. It initializes the I/O system and generally makes the environment safe for C++ programs; they're more fragile than assembly language programs and have to have everything set up for them before they can venture out.
To understand the necessity for the startup library, we have to take a look at the way variables are assigned to memory locations. So far, we have just assumed that a particular variable had a certain address, but how is this address determined in the real world?

There are several possible ways for this to occur; the particular one employed for any given variable is determined by the variable's storage class. The simplest of these is the static storage class; variables of this class are assigned memory addresses in the executable program when the program is linked. The most common way to put a variable in the static storage class is to define it outside any function.13 Such a variable will be initialized only once before main starts executing. We can specify the initial value if we wish; if we don't specify it, a default value (0 for numeric variables) will be assigned.14 An example of such a definition would be writing the line short x = 3; outside any function; this would cause x to be set to 3 before main starts executing. We can change the value of such a variable whenever we wish, just as with any other variable. The distinction I'm making here is that a static variable is always initialized before main begins executing. As you will see, this seemingly obvious characteristic of static variables is not shared with variables of other storage classes.

This idea of assigning storage at link time led to the following discussion with Susan:

Susan: If you declare a variable with only one value then it isn't a variable anymore, is it?
Steve: A static variable can have its value changed, so it's a genuine variable. I was saying that it's possible to specify what its initial value should be before main starts executing.
Susan: Are you trying to say where in memory this variable is to be stored? Isn't the compiler supposed to worry about that?
Steve: I'm not specifying a location, but rather an attribute of the variable. A static variable behaves differently from the "normal" variables that we've seen up till now. One difference is that a static variable is always initialized to a known value before main starts executing.

Why All Variables Aren't Automatically Initialized

The notion of storage classes is essential to the solution of another mystery. You may recall that I mentioned some time ago that C++ doesn't provide automatic initialization of all variables because that facility would make a program bigger and slower. I'll admit that the truth of this isn't intuitively obvious to the casual observer; after all, a variable (or more exactly, the storage location it occupies) has to have some value, so why not something reasonable? As we have just seen, this is done for static variables. However, there is another storage class for which such a facility isn't quite as easy or inexpensive to implement;15 that's the auto (short for "automatic") storage class, which is the default class used for variables defined in functions. An auto variable does not actually exist until the function in which it is defined starts execution and even then has no known value until we specifically assign a value to it.16

This notion led to a fair amount of discussion with Susan.

Susan: Then so far I know about two types of variables, static and auto, is this correct?
Steve: Right, those are the two "storage classes" that we've talked about so far.
Susan: The auto variables are made up of garbage and the static variables are made up of something understandable, right?
Steve: The auto variables are uninitialized by default, and the static variables are initialized to 0 (if they're numeric, at least).
Susan: When you say that the auto class is used for functions by default does this mean you don't use static ones ever?
Steve: Default means "what you get if you don't specify otherwise". For example, these days, if you buy almost any type of car and don't specify that you want a manual transmission, you will get an automatic, so automatic transmissions are the default.
Susan: So, since we have used auto variables up to this point, then I am confused when we initialize them to a value. If we do, would that not make them static?
Steve: This is a difficult topic, so it's not surprising that you're having trouble. I didn't realize quite how difficult it was until I tried to answer your question and ended up with the essay found under the heading "Automatic vs. Static Allocation" on page 268.
Susan: How do you know what the address will be to assign to a variable? OK, I mean this makes it sound like you, the programmer, know exactly which address in memory will be used for the variable and you assign it to that location.
Steve: You don't have to know the address; however, you do have to know that the address is fixed at link time. That's what makes it possible to initialize a static variable before main starts (if outside all functions), or just once the first time its definition is encountered (if inside a function). On the other hand, an auto variable can't be initialized until the beginning of each execution of the function in which it is defined, because its address can change between executions of the function.
Susan: I am having a hard time trying to figure out what you mean by using static variables outside functions. I have meant to ask you this, is main really a function even if you don't have anything to call? For example, in the first pumpkin weighing program, we didn't have to call another function but I have been wondering if main is really a function that just has nothing to call? So in that case, the variables used in main would be auto?
Steve: You are correct that main is a function. To be precise, it's the first function executed in any C++ program.17 If it calls other functions, that's fine, but it doesn't have to. As with any other function, the variables used in main are auto by default; in the case of the pumpkin weighing program, since we didn't make any of them static, they're all auto in fact.
So far, all of our variables have been auto, and in most programs the vast majority of all variables are of this class.18 Why should we use these variables when static ones have an initialization feature built in?

The first clue to this mystery is in the name auto. When we define a variable of the auto class, its address is assigned automatically when its function is entered; the address is valid for the duration of that function.19 Since the address of the variable isn't known until its function is entered, it can't be initialized until then, unlike the case with static variables. Therefore, if auto variables were automatically initialized, every function would have to start with some extra code to initialize every auto variable, which would make the program both slower and larger. Since Bjarne Stroustrup's design goals required that a C++ program should have the same run-time performance as a C program and as little space overhead as possible, such a feature was unacceptable. Luckily, forgetting to initialize an auto variable is something that can be detected at compile time, so it's possible for the compiler to warn us if we make this error. In general, it's a good idea to tell the compiler to warn you about dubious practices. Although not all of them may be real errors, some will be and this is by far the fastest and best way to find them.20

Now we've seen why auto variables aren't initialized by default: Their addresses aren't known until entering the function in which they're defined. But that doesn't explain the advantage of assigning the addresses then. Wouldn't it be simpler (and faster) to assign them all during the linking process, as is done with static variables?

Nested Functions

To understand why auto variables aren't assigned addresses during the linking process, we have to look at the way functions relate to one another. In particular, it is very common for a statement in one function to call another function; this is called nesting functions and can continue to any number of levels.

Susan explained this in her inimitable way:

Susan: Nesting of functions-does that mean a whole bunch of functions calling each other?
Steve: Not exactly. Usually functions don't call one another.21 Nesting of functions actually means one function calling another function, which in turn calls another function, and so on.
Although functions can and often do call other functions, it is very unlikely that every function in a large program will be in the midst of execution at any given time. This means that reserving space in the executable program for all of the variables in all of the functions will make that executable considerably larger than it otherwise would be.

If we had only static variables, this wasteful situation would indeed occur. The alternative, of course, is to use auto variables, which as we have just noted are assigned storage at run time. But where is that storage assigned, if not in the executable program?

While all static variables are assigned storage in the executable program when it is linked, auto variables are instead stored in a data structure called a stack; the name is intended to suggest the notion of stacking clean plates on a spring-loaded holder such as you might see in a cafeteria. The last plate deposited on the stack of plates will be the first one to be removed when a customer needs a fresh plate. Back in the world of programming, a stack with one entry might look something like Figure 5.7.

FIGURE 5.7. A stack with one entry

Name Value
TOP 1234

If we add (or push) another value on to the stack, say 999, the result would look like Figure 5.8.
FIGURE 5.8. A stack with two entries

Name Value
TOP 999
2nd 1234

If we were to push one more item with the value 1666, the result would look like Figure 5.9. Now, if we retrieve (or pop) a value, we'll get the one on top; namely 1666. Then the stack will look like it did in Figure 5.8. The next value to be popped off the stack will be the 999, leaving us with the situation in Figure 5.7 again. If we continue for one more round we'll get the value 1234, leaving us with an empty stack.
FIGURE 5.9. A stack wit
Name Value
TOP 1666
2nd 999
3rd 1234
h three entries
The reason that stacks are used to store auto variables is that the way items are pushed onto or popped off a stack exactly parallels what happens when one function calls another.

Susan had a question about stacks:

Susan: How many things can you push on the stack?
Steve: That depends on what kind of compiler you have, what operating system you are using and how much memory you have in your machine. With a 32-bit compiler running on a modern operating system and a modern PC, you might be able to store several million items on the stack before you run out of space.
Let's look at this stack idea again, but this time from the point of view of keeping track of where we are in one function when it calls another one, as well as allocating storage for auto variables.22

Returning to the Calling Function

In Figure 5.5, there are two calls to the function Average: The first one is used to average two weights and the other to average two ages. One point I didn't stress was exactly how the Average function "knew" which call was which; that is, how did Average return to the right place after each time it was called? In principle, the answer is fairly simple: The calling function somehow notifies the called function of the address of the next instruction that should be executed after the called function is finished (the return address). There are several possible ways to solve this problem. The simplest solution is to store the return address at some standardized position in the code of the called function; at the end of the called function, that address is used to get back to the caller. While this used to be standard practice, it has a number of drawbacks that have relegated it to the history books. A major problem with this approach is that it requires changing data that are stored with the code of the called routine. As we've already seen, when running a program on a modern CPU under a modern operating system, code and data areas of memory are treated differently and changing the contents of code areas at run time is not allowed.

Luckily, there is another convenient place to store return addresses: on the stack. This is such an important mechanism that all modern CPUs have a dedicated register, usually called the stack pointer, to make it easy and efficient to store and retrieve return addresses and other data that are of interest only during the execution of a particular function. In the case of the Intel CPUs, the stack pointer's name is esp.23 A machine instruction named call is designed to push the return address on the stack and jump to the beginning of the function being called.24 The call instruction isn't very complex in its operation, but before going into that explanation, you'll need some background information about how the CPU executes instructions.

How does the CPU "know" what instruction is the next to be executed? By using another dedicated register that we haven't discussed before, the program counter, which holds the address of the next instruction to be executed. Normally, this is the instruction physically following the one currently being executed; however, when we want to change the sequence of execution, as in an if statement or a function call, the program counter is loaded with the address of the instruction that logically follows the present one. Whatever instruction is at the address specified in the program counter is by definition the next instruction that will be executed; therefore, changing the address in the program counter to the address of any instruction causes that instruction to be the next one to be executed.

Here are the actual steps that the call instruction performs:

1. It saves the contents of the program counter on the stack.
2. Then it loads the program counter with the address of the first instruction of the called function.
What does this sequence of events achieve? Well, since the program counter always points to the next instruction to be executed, the address stored on the stack by the first step is the address of the next instruction after the call. Therefore, the last instruction in the called function can resume execution of the calling function by loading the program counter with the stored value on the stack. This will restart execution of the calling function at the next instruction after the call, which is exactly what we want to achieve.

The effect of the second step is to continue execution of the program with the first instruction of the called function; that's because the program counter is the register that specifies the address of the next instruction to be executed.

The Layout of Data in a Stack

As we'll see, the actual way that a stack is implemented is a bit different than is suggested by the "stack of plates" analogy, although the effect is exactly the same. Rather than keeping the top of the stack where it is and moving the data (a slow operation), the data are left where they are and the address stored in the stack pointer is changed, which is a much faster operation. In other words, whatever address the stack pointer is pointing to is by definition the top of the stack.

Before we get started on this analysis, here are some tips on how to interpret the stack diagrams. First, please note that the range of addresses that the stack occupies in these diagrams (given in hexadecimal) is arbitrary. The actual address where the stack is located in your program is determined by the linker and the operating system.

Next, the "Top of Stack" address, that is, the address where the stack pointer is pointing, will be in bold type. Also note that when we push items on the stack the stack pointer will move upward in the diagram. That's because lower addresses appear first in the diagram, and new items pushed onto the stack go at lower addresses. Anything in the diagram "above" the stack pointer (i.e., at a lower address than the stack pointer's current value) is not a meaningful value, as indicated in the "meaning" column.

Now let's suppose we start with an empty stack, with the stack pointer at 20001ffe, and ???? to indicate that we don't know the contents of that memory location. Thus, the stack will look like Figure 5.10.

FIGURE 5.10. An empty stack

Address Contents Meaning
20001ffe ???? none

Then, the user types in the two values "2" and "4" as the values of FirstWeight and SecondWeight, and the first call to Average occurs; let's suppose that call is at location 10001000. In that case, the actual sequence of events is something like this, although it will vary according to the compiler you are using:
1. The address of the next instruction to be executed (say, 10001005) is pushed onto the stack, along with the values for the arguments First and Second, which are copies of the arguments FirstWeight and SecondWeight. In the process, the CPU will subtract eight (the size of one address added to the size of two shorts, in bytes) from the stack pointer (which is then 20001ff6) and store the return address at the address currently pointed to by the stack pointer. Thus, the stack looks like Figure 5.11.
2. Execution starts in the Average function. However, before the code we write can be executed, we have to reserve space on the stack for the auto variable(s) defined in Average (other than the arguments First and Second, which have already been allocated); in this case, there is only one, namely Result. Since this variable is a short it takes 2 bytes, so the stack pointer has to be reduced by 2.
FIGURE 5.11.
Address Contents Meaning
20001ff2 ???? none
20001ff4 ???? none
20001ff6 10001005 return address in main
20001ffa 0004 Second
20001ffc 0002 First
20001ffe ???? none
The stack immediately after the call to Average

After this operation is completed, the stack will look like Figure 5.12
FIGURE 5.12.
Address Contents Meaning
20001ff2 ???? none
20001ff4 ???? Result
20001ff6 10001005 return address in main
20001ffa 0004 Second
20001ffc 0002 First
20001ffe ???? none
The stack after auto variable allocation

Wait a minute. What are those ???? doing at location 20001ff4? They represent an uninitialized memory location. We don't know what's in that location, because that depends on what it was used for previously, which could be almost anything. The C++ compiler uses stack-based addressing for auto variables, as well as copies of arguments passed in from the calling function. That is, the addresses of such variables are relative to the stack pointer, rather than being fixed addresses. In this case, the address of Result would be [esp], or the current value of the stack pointer; Second would be referred to in the object file as [esp+6] (i.e, 6 more than the current value of the stack pointer, to leave room for the return address and Result). Similarly, the address of First would be [esp+8], or 8 more than the current value of the stack pointer.25 Since the actual addresses occupied by these variables aren't known until the setup code at the beginning of the function is actually executed, there's no way to clear the variables out before then. That's why auto variables aren't automatically initialized.
As you might have guessed, Susan and I went over this in gory detail. Here's the play by play.
Susan: Yes, I think this is what has confused me in the past about functions. I never fully understood how the mechanism worked as how one function calls another. I still don't. But I guess it is by the position of the next address in a stack?
Steve: The stack is used to pass arguments and get return values from functions, but its most important use is to keep track of the return address where the calling function is supposed to continue after the called function is done.
Susan: This is how I am visualizing the use of the stack pointer. In one of my other books it showed how the clock worked in the CPU and it seemed to cycle by pointing in different directions as to what was to happen next in a program. So it was sort of a pointer. Is this how this pointer works? So let me get this straight. All CPUs have some kind of stack pointer, but they are used only for calling functions? Exactly where is the instruction call? It sounds to me like it is in the hardware, and I am having a very difficult time understanding how a piece of hardware can have an instruction.
Steve: All of the instructions executed in a program are executed by the hardware. The call instruction, in particular, does two things:
1. It saves the address of the next instruction (the contents of the program counter) on the stack.
2. It changes the program counter to point to the first instruction of the called function.
The return instruction is used to return to the calling function. It does this by the following steps:
1. It retrieves the saved value of the program counter from the stack.26
2. It sets the program counter back to that value.
The result of this is that execution of the program continues with the next instruction in the calling function.
Susan: Are you saying that, rather than the pointer of a stack actually pointing to the top of the physical stack, wherever it points to by definition will be the top of the stack, even if it really doesn't look like it?
Steve: Exactly.
Susan: Now I see why we have to know what a short is. So then the pointer is pointing to 20001ff4 as the top of the stack even though it doesn't look like it?
Steve: Absolutely correct.

5.6. Scope of Variables

Now let's look at another distinct way to categorize variables: the scope of a variable is the part of the program in which it can be accessed. Here, we are concerned with local scope and global scope. Variables with global scope are called global variables. These variables are defined outside any function and therefore by default can be accessed from any function.27 Global variables are always in the static storage class, as we have already seen. Variables with local scope are called local variables. These variables are defined in a function and are accessible only while that function is executing; they can be either static or auto and are auto by default.

Figure 5.13 shows the valid combinations of scope and storage class.

FIGURE 5.13. Scope vs. storage class

Scope Storage class

static auto
local
Y
Y
global
Y
N

Susan had some comments on this topic.
Susan: On this scope stuff; I think you are going to have to help me understand exactly what is inside a function and what is outside a function. I am not too sure I know what the difference is.
Steve: All code is inside a function. However, some variables (global variables) are outside all functions and therefore shareable by all functions. Variables that are defined inside functions are called local, because they are available only to the code that's in that function.
Susan: I am validating this with you now. Please correct any misconceptions.
1. Only variables are declared outside functions.
2. No code is written outside functions.
3. Up to this point I am not to be aware of anything else going on outside a function.
Steve: Correct.
Before we get deeper into the notion of scope, I think we should revisit the question of variable initialization in the light of the notion of global and local variables. This is a difficult topic, so it wouldn't be surprising if you don't find it obvious; Susan didn't. I wrote the next section to explain this topic to her.

Automatic vs. Static Allocation

What makes a variable static or auto is when its storage is assigned, and therefore when its address is known. In the case of a static variable, this happens at link time. In the case of an auto variable it happens when the function where it is defined is entered.28

This distinction affects initialization because it's impossible to initialize something until you know where it is. Therefore, an auto variable cannot be initialized until the function where it is defined is entered. This also means that you cannot assume that an auto variable will retain its value from one execution of the function where it's defined to the next execution of that function, because the variable might be at a different location the next time.

These restrictions do not apply to static variables, because their addresses are known at link time and don't change thereafter. A variable defined outside all functions (a global variable) is automatically in the static storage class, because otherwise its address would never be assigned.29 Since its address is known at link time, the initialization of such a variable can be and is performed before the start of main.

A static variable that is defined inside a function is different from one defined globally, in that it is not initialized until the function where it is defined is entered for the first time. However, its value is retained from one execution of its function to another, because its address is fixed rather than possibly varying from one call of the function to the next as can occur with an auto variable. For this property to be of use, the initialization of a static variable in a function must be performed only once; if it were performed on each entry to the function, the value from the previous execution would be lost. Therefore, that initialization is done only once, when the function is first entered.

Susan wanted some more explanation of what happens at link time and the related question of why we would want to use static variables.

Susan: Will you tell me again what happens at link time? Let's see, I think it goes like this: The source code is translated into an object file and then the object file is linked to the hardware to make executable code. Is that how it goes?
Steve: Not quite. The object file is linked with library files containing predefined functions in compiled form.
Susan: Now, tell me again why you would want a variable to be static? What is the advantage to that? Does it just take up less room and be more efficient?
Steve: The advantage is that a static variable keeps its value from one function call to the next. For example, suppose you wanted to count the number of times that a function was called. You could use a static variable in the function, initialize it to 0, and add 1 to it every time the function was called. When the program was done, the value of that variable would be the number of times the function was called. Obviously, we couldn't use an auto variable to do that, as it would have to be initialized every time the function started or we'd have an uninitialized variable.
Susan: I can see how using a static variable would work but I don't see why an auto variable couldn't do the same thing. Well, I guess it would change each time the function would be used. Are you saying in this case that the variable has to be global?
Steve: Not exactly. Although we could use a global variable, we could also use a static local variable. Figures 5.14 through 5.19 are some sample programs to illustrate the situation.
FIGURE 5.14. Using an auto variable and initializing it (code\count1.cpp)
#include <iostream>
using namespace std;

short counter()
{
short count = 0;

count ++;

cout << count << " ";

return 0;
}

int main()
{
short i;

for (i = 0; i < 10; i ++)
counter();

return 0;
}
FIGURE 5.15. Using an auto variable and not initializing it (code\count2.cpp)
#include <iostream>
using namespace std;

short counter()
{
short count;

count ++;

cout << count << " ";

return 0;
}

int main()
{
short i;

for (i = 0; i < 10; i ++)
counter();

return 0;
}
FIGURE 5.16. Using a local static variable and initializing it explicitly (code\count3.cpp)
#include <iostream>
using namespace std;

short counter()
{
static short count = 0;

count ++;

cout << count << " ";

return 0;
}

int main()
{
short i;

for (i = 0; i < 10; i ++)
counter();

return 0;
}
FIGURE 5.17. Using a local static variable and not initializing it explicitly (code\count4.cpp)
#include <iostream>
using namespace std;

short counter()
{
static short count;

count ++;

cout << count << " ";

return 0;
}

int main()
{
short i;

for (i = 0; i < 10; i ++)
counter();

return 0;
}

The Scope Resolution Operator

Let me interrupt the conversation here to point out something new in versions 5 and 6 of this little program: the "::" scope resolution operator. This is an example of what we were discussing in the section called "using, namespace, and std" on page 98 in Chapter 3. Susan's question was how we would refer to our own variables if they had the same names as variables in the standard library. The answer is to use the scope resolution operator to tell the compiler that we want to use global identifiers defined in our source code, not ones of the same name in the standard library. If we leave off the scope resolution operator in front of count in these programs, the compiler will report an error when it tries to compile them, because there is an identifier named count in the std namespace, and and the line using namespace std; at the beginning of each of our programs tells the compiler that we want to be able to access all the identifiers in the std namespace without specifying explicitly that they are from that namespace.

Now let's continue with the analysis of scope.

FIGURE 5.18. Using a global variable and initializing it explicitly (code\count5.cpp)
#include <iostream>
using namespace std;

short ::count = 0;

short counter()
{
::count ++;

cout << ::count << " ";

return 0;
}

int main()
{
short i;

for (i = 0; i < 10; i ++)
counter();

return 0;
}
FIGURE 5.19. Using a global variable and not initializing it explicitly (code\count6.cpp)
#include <iostream>
using namespace std;

short ::count;

short counter()
{
::count ++;

cout << ::count << " ";

return 0;
}

int main()
{
short i;

for (i = 0; i < 10; i ++)
counter();

return 0;
}
Steve: Now that I've cleared that up, what will each of these programs do when run?
Susan: Now, let me think about this. Variables are: static or auto, global or local.
Local is for use only within functions. These variables are mostly auto, and will be auto by default, but they can be static; in the latter case, they will be initialized to 0.
Steve: Correct; a static numeric variable is initialized to 0 if no other provision is made by the programmer to initialize it. One fine point: a local variable can be used only within one function.
Susan: Global variables are declared only outside functions. They are always allocated storage at link time, like static local variables.
Steve: Correct.
Susan: Variables with static allocation are fixed because they are initialized at link time and thus are done just once and never change. But they can be local or global.
Steve: Not exactly. The address of a statically allocated variable is set once and never changes; its value can change just like the value of an auto variable can.
Susan: That's what I had mixed up. I was confusing addresses with values.
Steve: OK, as long as you're straightened out now.
Susan: An auto variable is assigned an address when the function where it is defined is entered. All auto variables are local.
Steve: Correct.
Susan: Now, here is where I am confused. What is the difference between at link time and when the function where it is defined is entered? Does at link time mean when you are done with your source code and you are making an executable?
Steve: Yes.
Susan: And does when the function where it is defined is entered mean when the program is already made into an executable and you are running the program?
Steve: Yes.
Susan: I am confused about what we mean by initialization. I am confusing declaring a value for a variable and the designation of an address for a variable. It almost seems as if we are using these two meanings for the same term. I always thought that initializing a variable meant just assigning a value to it.
Steve: Initializing a variable means assigning an initial value to it. In the case of an auto variable, this must be done every time the function where it is declared is entered; whereas with a static variable, it is done once.
Susan: OK, this is what I imagined this to mean. Then how come, in your figure of a stack, you have values assigned to the places where the variables are?
Steve: Those values are present only after the variables to which they correspond have been initialized. In Figure 5.12, for example, the contents of the address corresponding to Result are shown as ???, rather than as a valid value; whereas the values of First and Second are shown as initialized, because they have already been set to values equal to the input arguments provided by the caller.
Susan: Remember when I started tracing the "sorting" program? It had random numbers in the places where numbers are supposed to go, and when I actually entered a value then those random numbers were replaced by that value. And is that why you put ??? there, because you know that something is there but you don't know exactly what? It is just whatever until you can put a real value into those slots.
Steve: Right.
Susan: So if you leave it alone by not initializing it, then it keeps the last value it had each time it goes through the loop and therefore the count goes up?
Steve: Yes, except that the initial value isn't reliable in that case. In the case of the example count programs, that value happened to be 0, but there's no reason to expect that in general.
Susan: I want you to know that it was not immediately apparent to me just what the code in the example programs was doing; it really does look kinda strange. Then I noticed that this code called a function named counter. Why? Couldn't this work without using a function call?
Steve: No, it wouldn't work without a function call, because the whole point is that when a function is called, the auto variables defined in that function have unknown values. How would I show that without a function call?
Susan: I see that. But I still don't get the point, because they all did the same thing except 1. The results for that were the following: 1 1 1 1 1 1 1 1 1 1. The results for the rest of the programs were all the same, being 1 2 3 4 5 6 7 8 9 10. So, if they all do the same thing, then what is the point? Now, what really makes me mad about this is why 1 has that result. This bothers me. Obviously it is not incrementing itself by 1 after the first increment, it is just staying at one. Oh, wait, okay, okay, maybe. . . how about this: If you initialize it to 0 then each time it comes up through the loop it is always 0 and then it will always add 1 to 0 and it has to do that 10 times.
Steve: Right, except that #2 does the same thing as the others only by accident; you got lucky and happened to have a 0 as the starting value of the uninitialized local variable in that example.
Susan: Then on the initialized local static variable, why does it work? Because it is static, and because its address is one place and won't budge; that means its value can increment. Well, would that mean it isn't written over in its location every time the function is called so we can add a value to it each time through?
Steve: Right.
Susan: And then the uninitialized static one works for the same reason the auto uninitialized one works.
Steve: Not quite. A static local variable is always initialized to something, just like a global variable is. If you don't specify an initial value for a static local numeric variable, it will be initialized to 0 during the first execution of the function where it is defined. On the other hand, as I mentioned above, you just got lucky with the uninitialized local variable example, which happened to have the starting value 0. Other people using other computers to run the program may have different results for that example.
Susan: Now as for global, this is hard. Let me guess. Do the global initialized and uninitialized work for the same reasons I said earlier?
Steve: The global variables are always initialized, whether you specify an initial value or not; if you don't specify one, it will be 0.
Susan: That's what you said about static numeric variables. Are they the same? Well, they have to be the same because only static variables can be global, right?
Steve: Correct. Global variables are always statically allocated.
Susan: So if you don't initialize a numeric variable then it can become any number unless it is a static numeric without explicit initialization and then it will be 0 by default?
Steve: Correct.
Susan: OK, let me see if I have this. All static really means is that the variable is put in an address of memory that can't be overwritten by another variable but can be overwritten when we change the variable's value?
Steve: Right.
Susan: These are tricks, and you know I don't like tricks. If they are global numeric variables, whether explicitly initialized or not, they are static; therefore they will at least have a value of 0. In example 5 this is stated explicitly but not in example 6, so the variable also will take the value of 0 by default, therefore these two programs are effectively identical, just like 3 and 4. That is why examples 3, 4, 5, and 6 have the same results.
Steve: Well, obviously the trick didn't work; you crossed me up by getting the right answers anyway.
Let's pause here to look at a sample program that has examples of all the types of variables and initialization states we've just discussed. These are:30
1. global, not explicitly initialized
2. global, explicitly initialized
3. auto, uninitialized
4. auto, initialized
5. local static, not explicitly initialized
6. local static, explicitly initialized
Careful examination of the sample program shown in Figure 5.20 will help you to visualize how and where each of these variable types might be used. As usual, you can compile and run it to see what it does; running it under the debugger is probably more helpful than running it directly.
FIGURE 5.20. Using variables of different scopes and storage classes (code\scopclas.cpp)
#include <iostream>
using namespace std;

short count1; // A global variable, not explicitly initialized
short count2 = 5; // A global variable, explicitly initialized

short func1()
{
short count3; // A local auto variable, not explicitly initialized
short count4 = 22; // A local auto variable, explicitly initialized
static short count5; // A local static variable, not explicitly initialized
static short count6 = 9; // A local static variable, explicitly initialized

count1 ++; // Incrementing the global variable count1.
count2 ++; // Incrementing the global variable count2.
count3 ++; // Incrementing the local uninitialized auto variable count3.
count4 ++; // Incrementing the local auto variable count4.
count5 ++; // Incrementing the local static variable count5.
count6 ++; // Incrementing the local static variable count6.

cout << "count1 = " << count1 << endl;
cout << "count2 = " << count2 << endl;
cout << "count3 = " << count3 << endl;
cout << "count4 = " << count4 << endl;
cout << "count5 = " << count5 << endl;
cout << "count6 = " << count6 << endl;
cout << endl;

return 0;
}

int main()
{
func1();
func1();

return 0;
}

FIGURE 5.21. The results of using variables of different scopes and storage classes (code\scopclas.out)
count1 = 1
count2 = 6
count3 = -32715
count4 = 23
count5 = 1
count6 = 10

count1 = 2
count2 = 7
count3 = -32715
count4 = 23
count5 = 2
count6 = 11


The results shown should help to answer the question of when we would want to use a static variable rather than an auto variable: whenever we need a variable that keeps its value from one execution of a function to another. You may be wondering where that weird value for count3 came from. Since we never initialized it, we can't complain when its value is meaningless. Although the compiler can warn us about such problems in some cases, they are still a significant source of errors in C++ programs, so it's worthwhile remembering to look out for them.

5.7. The Disadvantages of Global Variables

Now that we've cleared up the question of when different types of variables are initialized, let's continue with the distinction between global and local variables. You may be surprised that a programmer would accept the limitation of allowing certain variables to be accessed only in certain functions. Surely it's more powerful to be able to access anything anywhere. Isn't it?

Let me tell you a little story about the "power" of global variables. Unlike the one about the funny odometers, this one is true.

Using a Very Primitive BASIC Language

In the late 1970s, I worked for a (very) small software house where I developed a database program for the Radio Shack TRS-80 Model III computer. This computer was fairly powerful for the time; it had two 79K floppy disks and a maximum of 48K memory. The database program had to be able to find a subset of the few thousand records in the database in a minute or so. The speed of the floppy drive was the limiting factor. The only high-level language that was available was a BASIC interpreter clearly related by ancestry to QBASIC, the BASIC that comes with MS-DOS, but much more primitive; for example, variable names were limited to 2 characters.31 There was also an assembler, but even at that time I wasn't thrilled with the idea of writing a significant application program in assembly language. So we were stuck with BASIC.

Actually, that wasn't so bad. Even then, BASIC had pretty good string manipulation functions (much better than the ones that come with C) and the file access functions, although primitive, weren't too hard to work with for the application in question. You could read or write a fixed number of bytes anywhere in a disk file, and since all of the records in a given database were in fact the same length, that was good enough for our purposes. However, there were a couple of (related) glaring flaws in the language: there were no named subroutines (like functions in C++), and all variables were global.

Instead of names, subroutines were addressed by line number. In TRS-80 BASIC, each line had a number, and you could call a subroutine that started at line 1000 by writing "GOSUB 1000". At the end of the subroutine, a "RETURN" statement would cause control to return to the next statement after the GOSUB.

While this was functional in a moronic way, it had some serious drawbacks. First, of course, a number isn't as mnemonic as a name. Remembering that line 1000 is the beginning of the invoice printing routine, for example, isn't as easy as remembering the name PrintInvoice. In addition, if you "renumbered" the program to make room for inserting new lines between previously existing lines, the line numbers would change. The second drawback was that, as the example suggests, there was no way to pass arguments to a subroutine when it was called. Therefore, the only way for a subroutine to get input or produce output was by using and changing global variables. Yet another problem with this line-numbered subroutine facility was that you could call any line as a subroutine; no block structure such as we have in C++ was available to impose some order on the flow of control.

With such an arrangement, it was almost impossible to make a change anywhere in even a moderately large program without breaking some subroutine. One reason for this fragility was that a variable could be used or changed anywhere in the program; another was that it was impossible to identify subroutines except by adding comments to the program, which could be out of date. So almost any change could have effects throughout the program.

The Solution to My Problem with BASIC

After some time struggling with this problem, I decided to end it, once and for all, by adding named subroutines with arguments and local variables to the language. This made it possible to maintain the program and we ended up selling several hundred copies of it over a couple of years. Besides, fixing the language was fun.

The moral? There's almost always a way around a limitation of a computer language, although it may not be worth the effort to find it. Luckily, with C++, adding functionality is a bit easier than patching BASIC in assembly language.

A Scope Defines a namespace

Before we finish with our discussion of scope, I should point out something that may not be obvious to you. Every scope defines a new namespace. This is why local variables in different functions don't clash with one another: since every function has its own scope, it also has its own namespace, so a local variable in one function is distinct from any local variables in other functions that have the same name.

5.8. More on Using the Stack

Now it's time to get back to our analysis of the use of the stack in storing information needed during execution of a function. The next statement in our example program (Figure 5.5 on page 239) is Result = (First + Second) / 2;. Since we've assumed that First is 2, and Second is 4, the value of Result will be (4+2)/2, or 3. After this statement is executed, the stack looks like Figure 5.22.
FIGURE 5.22.
Address Contents Meaning
20001ff2 ???? none
20001ff4 0003 Result
20001ff6 10001005 return address in main
20001ffa 0004 Second
20001ffc 0002 First
20001ffe ???? none
The stack after the initialization of Result

Finally, at the end of the function, the stack pointer will be incremented to point to the stored return address. Then the return instruction will reload the program counter with the stored return address, which in this case is 10001005. Then the value of Result will be made available to the calling function and the stack pointer will be adjusted so the stack looks as it did before we called Average.

After the return, the stack will be empty as we no longer need the arguments, the auto variable Result, or the return address from the Average function. Figure 5.23 shows what the stack looks like now.

FIGURE 5.23.
Address Contents Meaning
20001ff2 ???? none
20001ff4 0003 none
20001ff6 10001005 none
20001ffa 0004 none
20001ffc 0002 none
20001ffe ???? none
The stack after exiting from Average

Do not be fooled by the casual statement "the stack is empty". That means only that the stack pointer (esp) is pointing to the same place it was when we started our excursion into the Average function; namely, 20001ffe. The values that were stored in the memory locations used by Average for its auto variables haven't been erased by changing the stack pointer. This illustrates one very good reason why we can't rely on the values of auto variables until they've been initialized; we don't know how the memory locations they occupy might have been used previously.

The previous discussion of how arguments are copied into local variables when a function is called applies directly to our Average function. If we try to change the input arguments, we will change only the copies of those arguments on the stack and the corresponding variables in the calling function won't be altered.32 That's perfectly acceptable here, since we don't want to change the values in the calling function; we just want to calculate their average and provide the result to the calling function. An argument that is handled this way is called a value argument, as its value is copied into a newly created variable in the called function, rather than allowing the called function access to the "real" argument in the calling function.33

One thing we haven't really discussed here is how the return value gets back to the caller. One way is to store it in a register, which is then available to the calling routine after we get back. This is a very easy and fast way to pass a return value back to the caller. However, it has a drawback: a register can hold only one value of 32 bits. Sometimes this is not enough, in which case another mechanism will be used. However, the compiler takes care of these details for us, so we don't have to worry about them.

5.9. Review

First, we added the fundamental programming concept of the function. A function is a piece of code that can "stand alone"; it can be compiled separately from other functions, and provides some service that we can use via a mechanism known as a function call. The function that makes the call is known as the calling function, and the one it calls is known as the called function. Before we can call a function, we need to know what input values it needs and what it returns. This information is provided by a function declaration at the beginning of each function. The function declaration includes an argument list, which specifies input values that the called function uses (if any), and a return type, which specifies the type of the value that it produces when it's finished (if any). When we call a function, it executes until it reaches the end of its code or reaches a return statement, whichever comes first. When either of these events happens, the program continues execution in the calling function immediately after the place where the function call occurs. Ordinarily, as in our example, an argument to a function is actually a copy of the variable in the calling program, so that the called function can't modify the "real" value in the caller. Such an argument is called a value argument.34

We also saw that function and variable names can be of any length, consisting of upper or lower case characters (or both), digits, and the special character underscore (_). To make it easier for the compiler to distinguish numbers from variable names, the first character can't be a digit. Also, a variable name can't be the same as a keyword, or name defined by the language; examples of keywords we've seen so far include if, for, and short.

After finishing the construction of our Average function, we saw how to use it by making a function call. Then we launched into an examination of the way that values in the calling function are converted into arguments in the called function, which required a detour into the software infrastructure.

We started this excursion by looking at the linker, which is used to construct programs from a number of functions compiled into separate object files. Next, we explored the notion of storage class, which determines the working lifetime of a variable. The simplest storage class is static. Variables of this class, which includes all variables defined outside any function, have storage assigned to them by the linker and retain the same address during the lifetime of the program. On the other hand, auto (for "automatic") variables are always defined in a function and are assigned storage on the stack when that function starts execution. The stack is the data structure that stores function arguments, local variables, and return addresses during the execution of a function; it's called that because it behaves like a spring-loaded stack of plates in a cafeteria, where the last one put on the top is the first one to be removed.

Then we noted that each variable, in addition to a storage class, has a scope, which is the part of a program in which the variable can be accessed. At this point, the scopes that are important to us are local scope and global scope. As you might guess, a global variable can be referred to anywhere, while a local variable can be accessed only in the function where it is defined. Although it may seem limiting to use local variables rather than global ones, programs that rely on global variables are very difficult to maintain, as a change anywhere can affect the rest of the program. Programs that limit the scope of their variables, on the other hand, minimize the amount of code that can be affected by a change in one place. Because local variables are only usable while in the function where they are defined, they can be stored on the stack; therefore, they don't occupy memory during the entire lifetime of the program.

Of course, local variables take up room while they're being used, which means that the stack has to have enough storage to hold all of the local variables for the current function and all of the functions that haven't finished executing. That is, the stack has to have enough room for all of the variables in the current function, the function that called the current function, the one that called that one, and so on up to the main function, which is always the top-level function in a C++ program. Since the amount of memory that is allocated to the stack is not unlimited, it's possible to run out of space, in which case your program will stop working. This is called a stack overflow, by analogy with what happens if you put too many plates on the cafeteria plate stack: it falls over and makes a mess. When using the compiler that comes with this book, it's unlikely that you'll ever run out of stack space unless you have a bug in your program. Some other compilers aren't as generous in their space allotments, so the likelihood of a stack overflow is less remote. The solution to this problem, should it arise, is to use another kind of storage allocation called dynamic storage; we'll see an example of this mechanism in Chapter 6.

Now that we've gone through that review, it's time to do an exercise to drive home some points about scope and storage classes.

5.10. Exercises

1. If the program in Figure 5.24 is run, what will be displayed?
FIGURE 5.24. Exercise 1 (code\calc1.cpp)
#include <iostream>
using namespace std;

short i;

short Calc(short x, short y)
{
static short j = 0;

cout << "The value of j in Calc is: " << j << endl;

i ++;

j = x + y + j;

return j;
}

int main()
{
short j;

for (i = 0; i < 5; i ++)
{
j = Calc(i + 5, i * 2) + 7;
cout << "The value of j in main is: " << j << endl;
}

return 0;
}

Answers to exercises can be found at the end of the chapter.

5.11. Conclusion

We've covered a lot of material in this chapter, ranging from the anatomy of functions through a lot more information about what's going on "underneath the covers" of even a fairly simple C++ program. Next, we'll see how to write a realistic, although simplified, application program using some more advanced concepts in C++.

5.12. Answers to Exercises

1. If you got this one right, congratulations! It's just filled with tricks, but they're all things that you might run into in a real (although poorly written) program. Here's the answer:

The value of j in Calc is: 0

The value of j in main is: 12

The value of j in Calc is: 5

The value of j in main is: 23

The value of j in Calc is: 16

The value of j in main is: 40

The first question is why there are only three values displayed by each output statement. The for loop that calls the Calc routine and displays the results should execute 5 times, shouldn't it?
This is the first trick. Since i is a global variable, the statement i ++; in the Calc function affects its value. Therefore, i starts out at 0 in the main function, as usual, but when the Calc function is called, i is incremented to 1. So the next time the modification expression i ++ in the for statement is executed, i is changed to 2. Now the controlled block of the for statement is executed again, with i set to 2. Again, the call to Calc results in i being incremented an extra time, to 3, so the next execution of the for loop sets i to 4. The final call to Calc increments the value of i to 5, so the for loop terminates, having executed only three times rather than the five you would expect by looking at it. Now you can see why global variables are dangerous!
Now what about the values that j takes on? Well, since the j in Calc is a static variable, it is initialized only once. Because it is a local static variable, that initialization is performed when Calc is called for the first time. So the first time Calc is called, j is set to 0. The arguments specified by main on the first call to Calc are 5 and 0; this means that, inside Calc, x and y have those values, respectively. Then the new value of j is calculated by the statement j = x + y + j;, or 5 in total. The return j; statement specifies this as the return value of Calc and this value is then added to 7 as specified by the assignment statement j = Calc(i + 5, i * 2) + 7; in main. That explains why the output statement in main displays the value of j as 12 the first time.
It's very important to note that the variable j in main is completely unrelated to the variable j in Calc. Since they are local variables, they have nothing in common but their names. There is no risk of confusion (at least on the compiler's part), since we can access a local variable only in the function in which it is defined. Therefore, when we refer to j in main, we mean the one defined there; and when we refer to j in Calc, we mean the one defined there.
Next, we call Calc again with the arguments 7 and 4. To compute these arguments from the expressions i + 5 and i * 2, you have to remember that i has been modified by Calc and is now 2, not 1 as we would expect normally. When we get to Calc, it displays the old value of j (5), left over from the previous execution of this function. This is because j is a local static variable; thus, the initialization statement static short j = 0; is executed only once, upon the first call to the function where it is defined. Once j has been set to a value in Calc, it will retain that value even in a subsequent call to Calc; this is quite unlike a normal auto variable, which has no known value at the beginning of execution of the function where it is defined. A new value of j is now calculated as 7 + 4 + 5, or 16, and returned to main.
On return from Calc, the value of j in main is 23, as set by the assignment statement j = Calc(i + 5, i * 2) + 7;. We also don't want to forget that i is now 3, having been changed in Calc.
Exactly the same steps occur for the last pass through the for loop: we call Calc with the new values of i + 5 and i * 2, which are 9 and 8, respectively, since i has been incremented to 4 by the for statement's modification expression i ++. Then Calc displays the old value of j, which is 16, and calculates the new value, which is 33. This is added to the literal value 7 and stored in j in main, resulting in the value 40, which is then displayed by the output statement.
Don't get discouraged if you didn't get this one, especially the effects caused by a global i. Even experienced programmers can be taken by surprise by programs that use global variables in such error-prone ways.

1 If you don't provide a return statement in a function that you're calling, then the called function will just return to the calling function when it gets to its closing }. However, this is not legal for a function that is defined to return a value. This of course leads to the question of why we'd call a function that doesn't return a value. One possibility is that the function exists only to produce output on the screen, rather than to return any results. The actions that a function performs other than returning a value are called side effects.

2 If you run this program under the debugger and look at the variables at the beginning of the program, please don't be confused by the seemingly random values that all of the variables start out with. These are just the garbage values that happen to be lying around in memory where those variables reside; as we've already seen, variables that haven't yet been assigned a value are called uninitialized variables. The variables in this program are all initialized before they are used, but you can look at them in the debugger before the initializing statements have been executed.

3 What do I mean by an incompatible type? C++ allows us, for example, to return a char variable where a short (or an int) is expected; the compiler will convert the char into either of those types for us automatically. This is convenient sometimes, but it reduces the chances of catching an error of this kind and therefore is less safe than it could be. This practice, called implicit conversion, is a legacy from C, which means that it can't be changed for practical reasons even though it is less than desirable theoretically.

4 The official name of the C++ standard is ISO/IEC 14882:1998(E).

5 You don't have to worry about wasting space in your program by using long identifiers. They go away when your program is compiled and are replaced by addresses of the variables or functions to which they refer.

6 For historical reasons, the underscore character _ counts as a letter. However, do not begin a variable or function name with an underscore, as such names are "reserved" for use by compiler writers and other language implementers.

7 This discussion might make you wonder whether there's another type of argument besides a value argument. There is, and we'll find out about it in Chapter 6.

8 This is done by the linker, which we'll get to later in this chapter.

9 Oxford English Dictionary, first current definition (4).

10 The alert reader may wonder why I referred to modules that have been "affected" rather than "changed". The reason is that even if we don't change a particular module, we must recompile it if a header file that it uses is changed. This is a serious maintenance problem in large systems but can be handled by special programming methods which are beyond the scope of this book.

11 You might say that files are "virtual"; that is, they're a figment of the operating system's imagination. Nonetheless, they are quite useful. This reminds me of the story about the man who went to a doctor, complaining that his brother had thought he was a hen for many years. The doctor asked why the family hadn't tried to help the brother before and the man replied, "We needed the eggs".

12 We've also used the vector part of the library indirectly, through my Vec type.

13 Another way to make a variable static is to state explicitly that the variable is static. However, this only works for variables defined inside functions. The keyword static is not used to specify that variables defined outside any function variable are statically allocated; since globals are always statically allocated, the keyword static means something different when applied to a global variable or function. Even though we won't be using static for global variables or functions, it's possible that you will run into it in other programs so it might be useful for you to have some idea of its meaning in those situations. An approximate translation of static for global functions or variables is that the function or variable is available for use only in the same file where it is defined, following the point of its definition.

14 You can count on this because it's part of the language definition, although it's nicer for the next programmer if you specify what you mean explicitly.

15 "Inexpensive", in programming parlance, means "not using up much time and/or space".

16 I'm oversimplifying a bit here. Variables can actually be declared inside any block, not just any function. An auto variable that is declared inside a block is born when the block is entered and lives until that block is finished executing. A static variable that is declared inside a block is initialized when that block is entered for the first time. It retains its value from that point on unless it is explicitly changed, as with any other statically allocated variable.

17 As I have noted before, this is actually an oversimplification. It is possible to write programs where some of our code is executed before the beginning of main. I'll explain how this can be done (and why) in the section entitled "Executing Code before the Beginning of main" on page 744, but we won't actually make use of such a facility in this book.

18 You may have noticed that we haven't defined any variables as auto. That's because any variables defined within a function and not marked as static are set to the default class, auto.

19 This is why variables defined outside a function are static rather than auto; if they were auto, when would their addresses be assigned?

20 There are also commercial tools that help locate errors of this type, as well as other errors, by analyzing the source code for inconsistencies.

21 There are cases in which functions call one another, e.g., one function calls a second function, which calls the first function. However, we won't see any examples of this recursion in this book.

22 The actual memory locations used to hold the items in the stack are just like any other locations in RAM; what makes them part of the stack is how they are used. Of course, as always, one memory location can hold only one item at a given time, so the locations used to hold entries on the stack cannot be simultaneously used for something else like machine instructions.

23 That's the 32-bit stack pointer. As in the case of the other registers, there's a 16-bit stack pointer called sp, which consists of the 16 lower bits of the "real" stack pointer esp.

24 That is, its name is call on Intel machines and many others; all modern CPUs have an equivalent instruction, although it may have a different name.

25 The actual mechanism used to refer to variables on the stack in a real compiler is likely to be different from this one and indeed can vary among compilers. However, this implementation is similar in principle to the mechanisms used by compiler writers.

26 Note that the compiler may be required to adjust the stack pointer before retrieving the saved value of the program counter, to allow for the space used by local variables in the function. In our example, we have to add 2 to the esp register to skip over the local variable storage and point to the return address saved on the stack.

27 Variables can be defined either inside a function (local variables) or outside a function (global variables); by contrast, code must always be inside a function.

28 By the way, if you were worried about keeping track of the address of every variable, that's the compiler's problem, not yours. The important distinction between a static and an auto variable is when the address is assigned, not what the actual address is.

29 Since all global variables are already in the static storage class, you don't have to (and shouldn't) use the keyword static when declaring a global variable. In another confusing legacy from C, the word static has an entirely different and unrelated meaning when used for a global variable.

30 Remember, there aren't any global auto variables, because they would never be initialized.

31 That is, two significant characters; you could have names as long as you wanted, but any two variables that had the same first two characters were actually the same variable.

32 Actually, it's generally a good idea not to change the values of arguments, even value arguments. Although this is legal, it tends to confuse programmers who read your code later as they often make the implicit assumption that arguments have the same value throughout a function.

33 It's also possible to define a function that has access to an actual variable in the calling function; we'll see how and when to do that at the appropriate time.

34 As this might suggest, there is another type of argument, which we'll get to in Chapter 6.


TOC PREV NEXT INDEX