Monday, January 17, 2011

Global variables

In the last lesson, you learned that variables declared inside a block have block scope. Block scope variables can only be accessed within the block in which they are declared (or a nested sub-block), and are destroyed when the block ends.
Variables declared outside of a block are called global variables. Global variables have program scope, which means they can be accessed everywhere in the program, and they are only destroyed when the program ends.
Here is an example of declaring a global variable:
01int g_nX; // global variable
02 
03int main()
04{
05    int nY; // local variable nY
06 
07    // global vars can be seen everywhere in program
08    // so we can change their values here
09    g_nX = 5;
10} // nY is destroyed here
Because global variables have program scope, they can be used across multiple files. In the section on programs with multiple files, you learned that in order to use a function declared in another file, you have to use a forward declaration, or a header file.
Similarly, in order to use a global variable that has been declared in another file, you have to use a forward declaration or a header file, along with the extern keyword. Extern tells the compiler that you are not declaring a new variable, but instead referring to a variable declared elsewhere.
Here is an example of using a forward declaration style extern:
global.cpp:
1// declaration of g_nValue
2int g_nValue = 5;
main.cpp:
1// extern tells the compiler this variable is declared elsewhere
2extern int g_nValue;
3 
4int main()
5{
6    g_nValue = 7;
7    return 0;
8}
Here is an example of using a header file extern:
global.cpp:
1// declaration of g_nValue
2int g_nValue = 5;
global.h:
1#ifndef GLOBAL_H // header guards
2#define GLOBAL_H
3 
4// extern tells the compiler this variable is declared elsewhere
5extern int g_nValue;
6 
7#endif
main.cpp:
1#include "global.h"
2int main()
3{
4    g_nValue = 7;
5    return 0;
6}
Generally speaking, if a global variable is going to be used in more than 2 files, it’s better to use the header file approach. Some programmers place all of a programs global variables in a file called globals.cpp, and create a header file named globals.h to be included by other .cpp files that need to use them.
Local variables with the same name as a global variable hide the global variable inside that block. However, the global scope operator (::) can be used to tell the compiler you mean the global version:
1int nValue = 5;
2 
3int main()
4{
5    int nValue = 7; // hides the global nValue variable
6    nValue++; // increments local nValue, not global nValue
7    ::nValue--; // decrements global nValue, not local nValue
8    return 0;
9} // local nValue is destroyed
However, having local variables with the same name as global variables is usually a recipe for trouble, and should be avoided whenever possible. Using Hungarian Notation, it is common to declare global variables with a “g_” prefix. This is an easy way to differentiate global variable from local variables, and avoid variables being hidden due to naming collisions.
New programmers are often tempted to use lots of global variables, because they are easy to work with, especially when many functions are involved. However, this is a very bad idea. In fact, global variables should generally be avoided completely!
Why global variables are evil
Global variables should be avoided for several reasons, but the primary reason is because they increase your program’s complexity immensely. For example, say you were examining a program and you wanted to know what a variable named g_nValue was used for. Because g_nValue is a global, and globals can be used anywhere in the entire program, you’d have to examine every single line of every single file! In a computer program with hundreds of files and millions of lines of code, you can imagine how long this would take!
Second, global variables are dangerous because their values can be changed by any function that is called, and there is no easy way for the programmer to know that this will happen. Consider the following program:
01// declare global variable
02int g_nMode = 1;
03 
04void doSomething()
05{
06    g_nMode = 2;
07}
08 
09int main()
10{
11    g_nMode = 1;
12 
13    doSomething();
14 
15    // Programmer expects g_nMode to be 1
16    // But doSomething changed it to 2!
17 
18    if (g_nMode == 1)
19        cout << "No threat detected." << endl;
20    else
21        cout << "Launching nuclear missiles..." << endl;
22 
23    return 0;
24}
Note that the programmer set g_nMode to 1, and then called doSomething(). Unless the programmer had explicit knowledge that doSomething() was going to change the value of g_nMode, he or she was probably not expecting doSomething() to change the value! Consequently, the rest of main() doesn’t work like the programmer expects (and the world is obliterated).
Global variables make every function call potentially dangerous, and the programmer has no easy way of knowing which ones are dangerous and which ones aren’t! Local variables are much safer because other functions can not affect them directly. Consequently, global variables should not be used unless there is a very good reason!

No comments: