Search

6.9 — 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 historically abused concepts in the language. Although they may seem harmless in small academic programs, they are often 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 calls to different functions are involved (passing data 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 usually 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 variable 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).

In short, global variables make the program’s state unpredictable. Every function call becomes 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:

After debugging, you determine that your program isn’t working correctly because g_mode has value 3, not 4. How do you fix it? Now you need to find all of the places g_mode could possibly be 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 key 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 accessed anywhere, you might have to look through the entire program to understand their usage. In small programs, this might not be an issue. In large ones, it will be.

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 actually functions.

Best practice

Use local variables instead of global variables whenever possible.

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

There aren’t many. In most 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.

A good example is a log file, where you can dump error or debug information. It probably makes sense to define this as a global, because you’re likely to only have one log in a program and it will likely be used everywhere in your program.

For what it’s worth, the std::cout and std::cin objects are implemented as global variables (inside the std namespace).

As a rule of thumb, any use of a global variable should meet at least the following two criteria: There should only ever be one of the thing the variable represents in your program, and its use should be ubiquitous throughout your program.

Many new programmers make the mistake of thinking that something can be implemented as a global because only one is needed right now. For example, you might think that because you’re implementing a single player game, you only need one player. But what happens later when you want to add a multiplayer mode (versus or hotseat)?

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. This advice isn’t only for non-const global variables, but can help with all global variables.

First, prefix all non-namespaced global variables with “g” or “g_”, or better yet, put them in a namespace (discussed in lesson 6.2 -- User-defined namespaces), to reduce the chance of naming collisions.

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 sure the variable can only be accessed from within the file it’s declared in, eg. by making the variable static or const. 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:

A reminder

const variables have internal linkage by default, gravity doesn’t need to be static.

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

Instead of:

Do this:

A joke

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

Answer: //

C++ jokes are the best.


6.10 -- Static local variables
Index
6.8 -- Global constants and inline variables

158 comments to 6.9 — Why global variables are evil

  • Edward

    "That way, if your function ever needs to use a different value for some circumstance, you can simply vary the argument. "

    Does it mean that if the function wants to use a different global variable or different value for the same global variable? because for the second one we could just change the literals used to initialized the global variable!

  • Edward

    How come we can "export" this function to other files?

    '// this function can be exported to other files to access the global outside of this file'

  • sami

    Hi,
    I am confused  a bit.

    You mentioned that, "if you find a good use for a non-const global variable". But all of your examples are about CONST global variables!why?

    "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"

    For example, instead of:

    Do this:

    • nascardriver

      > all of your examples are about CONST global variables!why?
      Because there aren't many good uses of non-const global variables. The advice given isn't only for non-const global variables, I updated the lesson.

  • Andrew Hislop

    Thank you. I got the "//" joke although it took a little while. Well done on all the other jokes though!

  • Austin

    Thank you for the lesson. Am I correct in assuming that non-const global variables mean global variables that do not use the keyword "const" or "constexpr"? If that is the case, could I substitute "non-const" global variables for "non-constant" global variables?

    Also for the last section, "Protecting yourself from global destruction" is it correct to assume that these pieces of advice apply to all global variables, i.e non-const (e.g variables that don't use the const and constexpr keyword) as well const global variables?

    I ask the last question because at first my understanding of the section was that is gives advice for non-const variables, but then I became confused when it used const variables in the example.

    Thank you!

    • nascardriver

      > Am I correct in assuming [...]
      Yes, you're right. Non-const is a common term in the C++ world.

      > is it correct to assume that these pieces of advice apply to all global variables
      these pieces of advice apply to all global variables

  • RJ

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

    You could remove: "of the" from the sentence: There should only ever be one "of the" thing the variable represents in your program, and its use should be ubiquitous throughout your program.

  • salah

    How can we use something like that??

    static const double gravity { 9.8 };

    as I know 'const' variables are internal linkage by default

    • Alex

      This is covered in the first example in lesson 6.8.

      • nascardriver

        I think the problem here is that the `static` is redundant, because `const` already causes internal linkage.

        • Alex

          Definitely redundant! The question is somewhat ambiguous -- I guessed that he was asking how we can use an internal linkage across multiple files, but perhaps I was incorrect.

          • koe

            I was wondering about this as well. The line "static const double gravity { 9.8 }; // has internal linkage, is accessible only by this file". Is there a reason for redundancy here? Could you add a comment to the section explaining why (or remove 'static' altogether)? Also, why wouldn't you use constexpr here?

            • nascardriver

              > Is there a reason for redundancy here?
              Nope, I removed it and added a reminder about `const`'s linkage.

              > why wouldn't you use constexpr here?
              `constexpr` is definitely the way to go. But with `constexpr`, the example wouldn't work, because it's never used with `extern`. This example applies if the type isn't `constexpr`, eg. `std::vector` before C++20. This example will likely be updated when we get new `constexpr` rules in C++20.

              • koe

                Now the previous paragraph says "First, make the variable static, so it can only be accessed directly in the file in which it is declared.". Maybe "First, make sure the variable has internal linkage (e.g. by making it static, const, or constexpr), so it can only be accessed directly in the file in which it is declared."

  • Nikola

    Hey guys. Still going through the tutorials when I get the time to do so. Noticed a mistake - minor and irrelevant to any examples - but I thought you might want to fix it.

    Section "So what are very good reasons to use non-const global variables?", second paragraph:
    "A good example is an log file,..." should be "A good example is a log file,..."

  • Agna

    Isn't this invalid because constexpr can't be forward declared (and it's not inline)? I think the example should be changed to "const" or clarify in the comment.

  • Chandler

    Parenthesis used in variable initialization instead of the { }

  • G-S

    "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!" -- could something like this not be fixed with shadowing>? for example for the nuclear missile example reinitializing the variable g_mode within the dosomething function like so int g_mode{} causes "missile" not to launch due to the shadowed value being hidden and g_mode(2) to be destroyed once the dosomething function is exited.

    • Alex

      Presumably doSomething() has a legitimate reason to change the global g_mode state. If it doesn't, it would be better off using a local variable named "mode", not a shadowing variable named "g_mode" that is actually a local variable.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]