Search

4.2a — Why global variables are evil

If you were to ask a veteran programmer for one piece of advice on good programming practices, after some thought, the most likely answer would be, “Avoid global variables!”. And with good reason: global variables are one of the most abused concepts in the language. Although they may seem harmless in small academic programs, they are often hugely problematic in larger ones.

New programmers are often tempted to use lots of global variables, because they are easy to work with, especially when many functions are involved (passing stuff through function parameters is a pain). However, this is generally a bad idea. Many developers believe non-const global variables should be avoided completely!

But before we go into why, we should make a clarification. When developers tell you that global variables are evil, they’re not talking about ALL global variables. They’re mostly talking about non-const global variables.

Why (non-const) global variables are evil

By far the biggest reason non-const global variables are dangerous is 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:

Note that the programmer set g_mode to 1, and then called doSomething(). Unless the programmer had explicit knowledge that doSomething() was going to change the value of g_mode, 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).

Non-const 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.

There are plenty of other good reasons not to use non-const globals.

With global variables, it’s not uncommon to find a piece of code that looks like this:

Your program is broken because g_mode is set to 3, not 4. How do you fix it? Now you need to find all of the places g_mode could possibly get set to 3, and trace through how it got set in the first place. It’s possible this may be in a totally unrelated piece of code!

One of the reasons to declare local variables as close to where they are used as possible is because doing so minimizes the amount of code you need to look through to understand what the variable does. Global variables are at the opposite end of the spectrum -- because they can be used anywhere, you might have to look through a significant amount of code to understand their usage.

For example, you might find g_mode is referenced 442 times in your program. Unless g_mode is well documented, you’ll potentially have to look through every use of g_mode to understand how it’s being used in different cases, what its valid values are, and what its overall function is.

Global variables also make your program less modular and less flexible. A function that utilizes nothing but its parameters and has no side effects is perfectly modular. Modularity helps both in understanding what a program does, as well as with reusability. Global variables reduce modularity significantly.

In particular, avoid using global variables for important “decision-point” variables (e.g. variables you’d use in a conditional statement, like variable g_mode in the example above). Your program isn’t likely to break if a global variable holding an informational value changes (e.g. like the user’s name). It is much more likely to break if you change a global variable that impacts how your program operates.

Rule: Use local variables instead of global variables whenever reasonable, and pass them to the functions that need them.

So what are very good reasons to use non-const global variables?

There aren’t many. In many cases, there are other ways to solve the problem that avoids the use of non-const global variables. But in some cases, judicious use of non-const global variables can actually reduce program complexity, and in these rare cases, their use may be better than the alternatives.

For example, if your program uses a database to read and write data, it may make sense to define the database globally, because it could be needed from anywhere. Similarly, if your program has an error log (or debug log) where you can dump error (or debug) information, it probably makes sense to define that globally, because you’re mostly likely to only have one log and it could be used anywhere. A sound library would be another good example: you probably don’t want to pass this to every function that needs it. Since you’ll probably only have one sound library managing all of your sounds, it may be better to declare it globally, initialize it at program launch, and then treat it as read-only thereafter.

Protecting yourself from global destruction

If you do find a good use for a non-const global variable, a few useful bits of advice will minimize the amount of trouble you can get into.

First, prefix all your global variables with “g_”, and/or put them in a namespace, both to reduce the chance of naming collisions and raise awareness that a variable is global.

For example, instead of:

Do this:

Second, instead of allowing direct access to the global variable, it’s a better practice to “encapsulate” the variable. First, make the variable static, so it can only be accessed directly in the file it’s declared. Second, provide external global “access functions” to work with the variable. These functions can ensure proper usage is maintained (e.g. do input validation, range checking, etc…). Also, if you ever decide to change the underlying implementation (e.g. move from one database to another), you only have to update the access functions instead of every piece of code that uses the global variable directly.

For example, instead of:

Do this:

Third, when writing a standalone function that uses the global variable, don’t use the variable directly in your function body. Pass it in as a parameter, and use the parameter. That way, if your function ever needs to use a different value for some circumstance, you can simply vary the parameter. This helps maintain modularity.

Instead of:

Do this:

Finally, changing the value of a global variable is the thing that is most likely to cause problems. Structure your code to assume a global variable’s value may change. Try to minimize the number of places where you change your global’s value -- treat the global as read-only as much as possible. If you can set your global’s value at program startup and then not change it afterward, you’ll minimize the chance of unexpected issues occurring.

A joke

What’s the best naming prefix for a global variable?

Answer: //

C++ jokes are the best.

Summary

Avoid use of non-const global variables if at all possible! If you do have to use them, use them sensibly and cautiously.

Const global variables (symbolic constants) are fine to use, so long as you use proper naming conventions.

In other cases, favor local variables. Pass those local variables to the functions that need them.

4.3 -- Static duration variables
Index
4.2 -- Global variables and linkage

79 comments to 4.2a — Why global variables are evil

  • Liam

    If you do need to set a global variable from within a function, what is the best way to do it? Through the function return? (I can see how this would make the program more modular, but it might still be hard to debug.)

    • Alex

      The best way is to have a separate function that sets the value of the global variable. Just call that function from your function with the value you want to assign the global variable.

  • Kushagra

    Second, instead of allowing direct access to the global variable, it’s a better practice to “encapsulate” the variable. First, make the variable static, so it can only be accessed directly in the file it’s declared. Second, provide external global “access functions” to work with the variable. These functions can ensure proper usage is maintained (e.g. do input validation, range checking, etc…). Also, if you ever decide to change the underlying implementation (e.g. move from one database to another), you only have to update the access functions instead of every piece of code that uses the global variable directly.

    What does this mean ?  and why it is encapsulated ? please explain it.

    • Alex

      In this case, we’re using the term “encapsulate” to mean that we’re disallowing direct access to the global variable, and instead forcing users to use a set of provided functions to access or work with the variable.

      There is a much more lengthy chat about what encapsulation is, and the benefits of using it, in the early part of chapter 8.

  • Kushagra

    For example, if your program uses a database to read and write data, it may make sense to define the database globally, because it could be needed from anywhere. Similarly, if your program has an error log (or debug log) where you can dump error (or debug) information, it probably makes sense to define that globally, because you’re mostly likely to only have one log and it could be used anywhere. A sound library would be another good example: you probably don’t want to pass this to every function that needs it. Since you’ll probably only have one sound library managing all of your sounds, it may be better to declare it globally, initialize it at program launch, and then treat it as read-only thereafter.  

    I don’t know what database, error log or sound library is, but as you said they can be used anywhere in the file , then why don’t we use constant global variables?

    • Alex

      Well, you probably wouldn’t want your database, error log, or sound library to be constant, because you probably need to modify them (write entries to the database, log messages to the error log, or load sounds into the sound library). But it might make sense to make them a non-const global variable if they’re truly used everywhere and you’ll only ever have one instance.

  • Kushagra

    // declare global variable
    int g_mode;

    void doSomething()
    {
        g_mode = 2; // set the global g_mode variable to 2
    }

    int main()
    {
        g_mode = 1; // note: this sets the global g_mode variable to 1.  It does not declare a local g_mode variable!

        doSomething();

        // Programmer still expects g_mode to be 1
        // But doSomething changed it to 2!

        if (g_mode == 1)
            std::cout << "No threat detected.n";
        else
            std::cout << "Launching nuclear missiles…n";

        return 0;
    }

    In this , why the programmer defined g_mode in dosomething when he knew that he would require to change this value .

    • Alex

      It’s an academic example to show how global variables can lead to unexpected consequences if you’re not completely aware of what your program is doing. Clearly in this short program it’s obvious what’s going to happen -- but in a much more complicated program, that isn’t always so obvious (especially if someone else wrote doSomething(), and if g_mode = 2 is buried somewhere in the middle of the function).

  • Hema

    Hi. This is a very silly question. You have written- double getGravity() // this function can be exported to other files to access the global outside of this file-how can double getGravity() be a function?

    • Alex

      double getGravity(); is a function prototype for a function named getGravity that takes no parameters and returns a double.

      The compiler knows to parse it this way due to the parenthesis attached to the identifier.

  • AMG

    Alex,
    1. Does function execution time depend on number of parameters, which are passed to a function? Function with 2 parameters vs function with 10?
    2. I know memory allocation takes time, so a large global "utility" buffer for a high res image should save time on memory reallocation. However, globals may block parallel execution. How about globals and multi-threading?
    3. Is avoiding globals a win-win scenario, or we may lose sometimes?

    • Alex

      1) Yes, but the impact is so tiny that you shouldn’t worry about it. You’re better off focusing on ensuring you aren’t making unncessary copies of arguments or return values.
      2) Yes, sometimes programs will make a single large request for memory from the operating system and then parcel that our internally. You generally won’t need to do this unless you’re working on a high performance game or simulation. It’s definitely outside the scope of these tutorials.
      3) No. Like almost anything, there is a time and a place for using a global. Just make sure you use safe practices when you do.

  • Sam

    There are plenty of great reasons to avoid them wherever possible, granted, but the article doesn’t touch on any potential alternatives.  Maybe that’s a little out of scope, but stating "this is bad" without "do that instead" might not be terribly useful to everyone.  What ARE the good practices for storing information between chains of functions?

    • Alex

      You really have two options:
      1) Define your data locally and pass it to the functions that need it (structs and classes help keep this manageable, and passing by reference helps avoid making copies)
      2) Use global variables anyway (and encapsulate them if you can)

      I haven’t covered structs, classes, or references at this point, which is why I don’t talk explicitly about them. But in their absence, you can always just pass lots of fundamental values by value.

  • Piyush

    If programmer did not know what dosomething does why did he invoke it in the first place.
    Shouldn’t it be the fault of the programmer .
    Why take away such a useful tool

    • Alex

      It’s easy to see what g_mode does in this case because the example is trivial. In a huge program with hundreds or thousands of functions, it’s very easy to think you’re calling a function to perform a particular task and not realize it has some other side effect, and have that side effect bite you later on (causing unexpected behavior, or maybe a crash).

      Nobody is taking away global variables -- you can use them if and when you have a legitimate use case for using them. However, for the most part, you should avoid them, because side effects are dangerous, and the more you can minimize the scope of the things you’re changing, the less likely you’ll accidentally break something.

  • Eric

    Alex,

    Under the heading "Why (non-const) global variables are evil" there are two cout calls that need to be updated to std::cout in the first example (in the example that results in global destruction -- nice subliminal pun about the evils of global variables).

    Thanks again for all you do!

  • arun

    A class has been declared globally but main() is unable to access its functions.why?

    • Alex

      main() should be able to access the functions of a global class, so if it didn’t work, then something else went wrong. Most likely either main() isn’t seeing the class, or the class wasn’t instantiated properly.

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter