Name Scope

Note

This has nothing to do with mouthwash!

Now that we are building programs with more than one module, and can start to talk about the right way to organize a bigger program, we need to introduce the concept of name scope. Simply put, this is a concept that defines where in a program we can refer to a name. That name can be the name of a variable, constant, or module.

Long ago, I used an analogy to explain name scope. The analogy involved surrounding parts of your program with one way mirrors. You know how these work, from one side you cannot see through them, but from the other side you can see through them. We will surround the entire program file in such a mirror, and further, we will surround each module we create in that file in another mirror. The mirrors will be set up so that from outside of the file, you cannot look into the file, and from outside of any module (but inside the file), you cannot see into the function. If you are standing inside the function, you can see out of the function to the world outside of the function, maybe even outside of the file.

One catch

There is one catch to this rule. You are only allowed to look upward in your program, never downward. As you read your program code, you will define names of variables and constants which hold your data. We have already heard the rule that you cannot use a name unless the compiler knows everything it need to know about that name so it can make sure you use it correctly.

You may also define modules in your code, either fully, or in two parts: the prototype followed by the full module definition. The scope rules determine where in our program we can refer to the module name, meaning where we can call the module into action! You can pretend that each module has the name of the module painted on the outside of the enclosing mirror.

Here is how scope works.

Scope

At any point in your program code where you want to use a name, we need to look upward in the program to see if that name has been defined above us. If so, we are allowed to use the name If not, the compiler will generate an error.

If we have modules in the program between where we want to use the name, we cannot see into those modules, but we can see around them to code above the modules surrounding mirror. Again, if the name can be found using that set of rules, we can use the name.

Module local names

Names created in a module are called local, meaning they are only visible to code inside that module. This protects them from accidentally being used by any other code in the program. We want this to allow us to move the module into another program without breaking anything in that new program.

What is interesting about modules, though, is that they can use names of variables and constants outside of their surrounding mirrors. We call these names global since they must be defined outside of any module to be seen. Normally, we place such names at the top of the program which makes then visible to any code below the point where they are defined. The global term indicates that you can use those names anywhere in your program.

Modules can see other modules as well, so a module can call another module if needed. This is how we build large programs, dividing them up into a bunch of modules that activate each other as needed to do the required work.

Module parameter names

The names of parameters we create for our modules are actually local to the module, although it is common to see those names in a prototype, or module definition. The names themselves can only be used inside the module, and act like specially initialized variables that code in the module can use. When some piece of code calls the module, those parameter names are initialized with the right values at that moment. The caller’s code determines what value will be placed in those variables, and the module code will not be aware how that all happened!

Here is an example program, just to reinforce this scope concept:

#include <iostream>     // makes a bunch of names "global" (like cout, and cin)
using namespace std;    // simplifies some names

const double PI = acos(-1.0);     // define a "global" constant named PI

void myfunction(double angle) {
    double radians;                 // uninitialized local variable

    radians = angle * PI / 180.0;   // using global PI, OK since it is above here

    cout << "Radians:" << radians << endl;  // using several globals and a local
}

int main(int argc, char ** argv) {
    cout << "Hello, there!" << endl;        // global cout used here

    myfunction(45.0);                       // calling module defined above. angle will be 45.0

}

This code shows several examples of using names defined above. In module main, we cannot use the name radians since it is invisible to us. It is hiden inside the morrit that surrounds myfunction. We can call myfunction, since we can see its name.

The analogy is important in organizing code. It i common in several languages, but not all. You will need to learn the specific rules for scope in what ever language you end up using!