Scope
One concept that is important for a programmer to understand is scope. Simply stated, scope is the lifetime and visibility a variable has in a program. There are essentially three basic types of scope:
- global
- function (also called local)
- statement block
We'll consider each of these individually.
Global Scope
For a variable to have global scope, the variable must be defined outside of a function block. We can define a variable with global scope as follows.
Definition: A variable has global scope if the definition of the variable is made
outside of all program functions
Properties:
- A variable with global scope is visible from its point of definition to the end
of the file in which it is defined - A variable with global scope has a lifetime that exists from the time the
program is loaded until the program terminates
What do we mean by the phrase “point of definition”? Consider the following program:
int val; // <--- val starts its life with the semicolon
void setup() {
// put your setup code here, to run once:
val = 10;
}
void loop() {
// put your main code here, to run repeatedly:
}
// <--- val ends its life here, the end of the source code file
At the top of the program a variable named val is defined. Because it is defined outside of any function (e.g., the setup() or loop() functions), val has global scope. For discussion purposes, val's lifetime starts when the compiler sees the semicolon at the end of the int val statement. Before the next statement is read by the compiler, the compiler has created an entry for val in its symbol table. A compiler symbol table is used to record the details about a variable when its lifetime starts. For our discussion, the important details are 1) its type (an int), 2) its name (val), 3) its scope (global, because of where it is defined), and 4) that memory is allocated for val. If you try to define a variable with the same name at the same scope level, you will get a “duplicate definition” error.
It is important to note that the scope of val extends from its point of definition to the end of the source code file in which it is defined. In the next example, we define a second variable, num, at the bottom of the file, but we try to use it in setup():
int val; // <--- val starts its life with the semicolon
void setup() {
// put your setup code here, to run once:
val = 10;
num = 50; // Error here!!
}
void loop() {
// put your main code here, to run repeatedly:
}
int num; // Global scope, but no code after its definition...worthless
// <--- val ends its life here, the end of the source code file
If you try to compile this program, you get an error message on the assignment statement for num in setup() which says: “ 'num' was not declared in this scope”. (Actually, the error message should say “num is not defined in this scope”. As we shall see later, define and declare are two different concepts and many programmers are sloppy about making the distinction.) The important thing to notice, however, is that our code tries to use num before it is defined. Since the definition of num doesn't occur until the end of the source code file, it comes to life for about a nano second and then the end of the file is read and it dies. Putting the definition of num at the end of the file does no good since it doesn't come into scope until the last point in the program. If you move the definition of num to a new line just below the definition of val, the program compiles without error.
Pros and Cons of Global Scope
Pros: Global scope does make it easier for all aspects of your program to have access to a variable. If you define a variable at the top of the program with global scope, every statement in your source code file can use/change the value of that globally-defined variable. For example:
int val;
void setup() {
Serial.begin(9600);
val = 10;
SquareIt();
Serial.print("The square is: ");
Serial.println(val);
}
void loop() {
}
void SquareIt()
{
val = val * val;
}
In this example, we wrote a new function named SquareIt() that takes the value of val and multiplies it by itself. Because val has global scope, the SquareIt() function knows where val lives in memory, can fetch its value and square it. The conclusion is that global scope makes it easy for other parts of the program to access that variable.
Cons: Global scope makes it too easy for all aspects of your program to have access to a variable. For example, suppose you have a large program with several thousand program statements in it and a variable with global scope. For reasons you can't explain, that variable takes on an unexpected value when the program is run. Because every statement can “see” and “use” that global variable, you don't really know where to start looking for the program error. The problem could be anywhere that variable is in scope!
A global variable is like putting a prostitute at the top of your program and then giving every assignment statement in your program a $50 bill. You don't know who is the father of the error is or where that error was conceived. Global scope makes it more difficult to find and correct a program error (in a process called debugging) simply because every statement in the program can see and modify the global variable. As a result, it is desirable to limit the number of variables defined with global scope as much as possible. The process of limiting the scope of a variable is called encapsulation. The more you can encapsulate a variable, the easier it is to debug a program.