CLOSE

Global variables in C/C++ are variables that are declared outside of all functions, typically at the top of a source file. Unlike local variables, they have broader scope, longer duration, and external linkage by default, making them accessible throughout the program and across multiple files. However, their convenience comes with risks and best practices must be followed.

Properties of Global Variables

PropertyDescription
ScopeAccessible from any function in the same file; across files with extern.
LifetimeExists for the entire duration of the program—from start to end.
LinkageExternal by default; internal if declared with static.
+---------------------------------------------------+
|                  Global Variable                  |
+---------------------------------------------------+
| Scope:                                            |
| - Accessible from any function in the same file.  |
| - Can be accessed across files with 'extern'.     |
+---------------------------------------------------+
| Duration (Lifetime):                              |
| - Exists for the entire duration of the program.  |
| - Initialized at program start; destroyed at end. |
+---------------------------------------------------+
| Linkage:                                          |
| - External linkage by default (accessible across  |
|   files).                                         |
| - Internal linkage when declared with 'static'    |
|   (limited to file).                              |
+---------------------------------------------------+

Declaring of Global Variables

Global variables are typically declared at the top of a file, below the includes, in the global namespace. Here's an example of declaring a global variable:

#include <iostream>

// Global variable declaration
int globalVar = 10;

void function1() {
    globalVar += 5;  // Accessing and modifying globalVar
}

void function2() {
    std::cout << "globalVar: " << globalVar << std::endl;  // Output: 15
}

int main() {
    function1();
    function2();
    return 0;
}

In the example above:

  • globalVar is a global variable and is accessible in both function1 and function2. Any change made to globalVar in one function is visible in other functions.

Scope of Global Variables

  • Global variables have global namespace scope.
  • Can be used anywhere in the file from the point of declaration onward.

Global variables can also be defined inside a user-defined namespace, but their scope remains global:

#include <iostream>

namespace Foo {
    int g_x {}; // g_x is now inside the Foo namespace but is still a global variable
}

void doSomething() {
    // Global variables can be seen and used everywhere in the file
    Foo::g_x = 3;
    std::cout << Foo::g_x << '\n';
}

int main() {
    doSomething();
    std::cout << Foo::g_x << '\n';

    // Global variables can be seen and used everywhere in the file
    Foo::g_x = 5;
    std::cout << Foo::g_x << '\n';

    return 0;
}

Duration (Lifetime)

  • Global variables have static duration
  • They are created when the program starts and destroyed when it ends.
  • They persist in memory throughout the program's life.
  • Global variables are automatically initialized to zero if not explicitly initialized (for basic types like int, char, etc.).

Example:

#include <iostream>

int globalVar;  // Implicitly initialized to 0

int main() {
    std::cout << "Global variable: " << globalVar << std::endl;  // Output: 0
    return 0;
}

Linkage of Global Variables

✅ External Linkage (default)

Global variables have external linkage by default. This means they can be shared across multiple source files by using the extern keyword.

  • Internal linkage can be achieved by using the static keyword with global variables. This limits the scope of the variable to the file in which it is declared.

External Linkage Example (across multiple files):

In file1.cpp:

int globalVar = 100;  // Global variable definition

void incrementGlobal() {
    globalVar++;
}

In file2.cpp:

#include <iostream>

extern int globalVar;  // Declare globalVar (extern) to use it across files

void printGlobal() {
    std::cout << "globalVar: " << globalVar << std::endl;  // Access the global variable defined in file1.cpp
}

In this example:

  • globalVar is defined in file1.cpp.
  • The extern keyword in file2.cpp allows access to globalVar from the other file.

🔒 Internal Linkage with static

If you declare a global variable with the static keyword, its linkage is restricted to the file in which it is declared, preventing it from being accessed by other files.

Example:

// file1.cpp
static int file1Var = 10;  // Internal linkage

void modifyVar() {
    file1Var += 5;  // Only accessible in this file
}

// file2.cpp
extern int file1Var;  // This would cause a compilation error because file1Var has internal linkage (static)

Here, file1Var is declared as static, making it accessible only within file1.cpp and preventing other files from accessing it.

Naming Global Variables

To avoid confusion:

By convention, global variables are often prefixed with “g” or “g_” to indicate that they are global.

This practice helps avoid naming collisions, prevent inadvertent name shadowing, and signifies that the variables persists beyond the scope of functions.

int g_globalVariable;    // Non-const global variable without prefix
int g_globalVariable2;   // Another global variable without prefix

const int g_constVariable;    // Error: Constant global variable must be initialized
constexpr int g_constexprVariable;   // Error: Constexpr global variable must be initialized

const int g_globalConstVariable { 42 };  // Initialized global const variable
constexpr int g_globalConstexprVariable { 99 };  // Initialized global constexpr variable

Initialization of Global Variables

Unlike local variables, global variables with static duration are zero-initialized by default.

Non-const global can be optionally initialized:

int g_x;       // Non-constant global variable (zero-initialized by default)
int g_y {};    // Explicitly value-initialized global variable (resulting in zero-initialization)
int g_z { 1 }; // List-initialized with a specific value

The Problem with (non-const) Global Variables

  • The biggest issue is their ability to be changed by any function leading to unpredictable program state.
  • Example: Changing a global variable without the programmer's knowledge can lead to unexpected outcomes.
int g_mode;

void doSomething() {
    g_mode = 2;
}

int main() {
    g_mode = 1;
    doSomething();
    
    if (g_mode == 1) {
        // Programmer expects g_mode to be 1, but it's changed to 2
        std::cout << "No threat detected.\n";
    } else {
        std::cout << "Launching nuclear missiles...\n";
    }
    
    return 0;
}

Drawbacks of (non-const) Global Variables

  • Debugging challenges: Locating all places where a global variable is modified can be difficult.
  • Reduced modularity: Global variables make the program less modular and flexible.
  • Difficulty in understanding: The usage of global variables may require examining the entire program.

Best Practice: Avoid (non-const) Global Variables:

  • The advice is to use local variables instead of global variables whenever possible.
  • Global variables should only be used if there's a single instance of what the variable represents.

Pros/Cons of Global Variables

✔️ Pros:

  1. Data Sharing: Global variables can be used to share data between different functions or even across different source files.
  2. Convenience: Global variables can be accessed and modified from anywhere in the program, simplifying certain designs (e.g., managing a counter or status flag).
  3. Initialization: Global variables are automatically initialized to zero if no initial value is provided, which can be convenient in some cases.

❌ Cons:

  1. Potential for Unintended Side Effects: Since global variables can be modified from anywhere in the program, it becomes difficult to track where changes to the variable occur, leading to unintended behavior.
  2. Reduced Modularity: Global variables make the program less modular since different functions are tied together through shared state. This can reduce code reusability.
  3. Namespace Pollution: Global variables can pollute the namespace, increasing the risk of name collisions and making it harder to maintain the program.
  4. Harder to Debug: It becomes harder to debug and maintain programs with many global variables because changes made in one part of the code can have unexpected consequences in another part.

✅ Best Practices for Global Variables

  1. Minimize Use:
    • Use global variables sparingly. If a variable can be declared as local or passed as a function parameter, prefer that approach to enhance modularity and encapsulation.
  2. Use Descriptive Names:
    • Choose clear and meaningful names for global variables. This helps avoid confusion and makes the purpose of the variable evident.
  3. Limit Scope with static:
    • When a global variable is needed only within a single file, declare it as static. This limits its visibility and prevents it from being accessed from other files.

      static int internalCounter;  // Accessible only within this file
      
  4. Initialize Global Variables Properly:
    • Always initialize global variables explicitly, if possible, to avoid undefined behavior. Uninitialized global variables are set to zero by default, but explicit initialization is clearer.

      int globalVar = 10;  // Explicit initialization
      
  5. Use a Namespace:
    • In C++, consider placing global variables in a namespace to avoid name collisions and clarify their context.

      namespace AppSettings {
          int screenWidth = 800;
          int screenHeight = 600;
      }
      
  6. Document Usage:
    • Comment on the purpose and intended use of global variables. This documentation can help other developers (or your future self) understand why the variable exists and how it should be used.