The issue is less that they're bad, and more that they're dangerous. They have their own set of pros and cons, and there are situations where they're either the most efficient or only way to achieve a particular task. However, they're very easy to misuse, even if you take steps to always use them properly.
A few pros:
- Can be accessed from any function.
- Can be accessed from multiple threads.
- Will never go out of scope until the program ends.
A few cons:
- Can be accessed from any function, without needing to be explicitly dragged in as a parameter and/or documented.
- Not thread-safe.
- Pollutes the global namespace and potentially causes name collisions, unless measures are taken to prevent this.
Note, if you will, that the first two pros and the first two cons I listed are the exact same thing, just with different wording. This is because the features of a global variable can indeed be useful, but the very features that make them useful are the source of all their problems.
A few potential solutions to some of the problems:
- Consider whether they're actually the best or most efficient solution for the problem. If there are any better solutions, use that instead.
- Put them in a namespace [C++] or singleton struct [C, C++] with a unique name (a good example would be
Globals
or GlobalVars
), or use a standardised naming convention for global variables (such as global_[name]
or g_module_varNameStyle
(as mentioned by underscore_d in the comments)). This will both document their use (you can find code that uses global variables by searching for the namespace/struct name), and minimise the impact on the global namespace.
- For any function that accesses global variables, explicitly document which variables it reads and which it writes. This will make troubleshooting easier.
- Put them in their own source file and declare them
extern
in the associated header, so their use can be limited to compilation units that need to access them. If your code relies on a lot of global variables, but each compilation unit only needs access to a handful of them, you could consider sorting them into multiple source files, so it's easier to limit each file's access to global variables.
- Set up a mechanism to lock and unlock them, and/or design your code so that as few functions as possible need to actually modify global variables. Reading them is a lot safer than writing them, although thread races may still cause problems in multithreaded programs.
- Basically, minimise access to them, and maximise name uniqueness. You want to avoid name collisions and have as few functions as possible that can potentially modify any given variable.
Whether they're good or bad depends on how you use them. The majority tend to use them badly, hence the general wariness towards them. If used properly, they can be a major boon; if used poorly, however, they can and will come back to bite you when and how you least expect it.
A good way to look at it is that they themselves aren't bad, but they enable bad design, and can multiply the effects of bad design exponentially.
Even if you don't intend to use them, it is better to know how to use them safely and choose not to, than not to use them because you don't know how to use them safely. If you ever find yourself in a situation where you need to maintain pre-existing code that relies on global variables, you may be in for difficulty if you don't know how to use them properly.