7-4 全局变量介绍
在第7.3节——局部变量中,我们讨论了局部变量是在函数体内定义的变量。局部变量具有块作用域(仅在其声明所在的代码块内可见),并具有自动生存期(在定义点创建,离开代码块时销毁)。
在C++中,变量也可以在函数外部声明。此类变量称为全局变量global variables。
声明全局变量
按惯例,全局变量应在文件开头、包含语句之后、全局命名空间内声明。以下是一个全局变量定义示例:
#include <iostream>
// Variables declared outside of a function are global variables
int g_x {}; // global variable g_x
void doSomething()
{
// global variables can be seen and used everywhere in the file
g_x = 3;
std::cout << g_x << '\n';
}
int main()
{
doSomething();
std::cout << g_x << '\n';
// global variables can be seen and used everywhere in the file
g_x = 5;
std::cout << g_x << '\n';
return 0;
}
// g_x goes out of scope here
上述示例输出:

全局变量的作用域
在全局命名空间中声明的标识符具有全局命名空间作用域global namespace scope(通常称为全局作用域global scope,有时也非正式地称为文件作用域file scope),这意味着它们从声明点起直至声明所在文件的结尾都可见。
全局变量声明后,即可在文件内任意位置使用!在上例中,全局变量 g_x 同时被 doSomething() 和 main() 函数调用。
全局变量也可定义在用户自定义命名空间内。下例与前例相同,但将 g_x 从全局作用域移入用户定义的 Foo 命名空间:
#include <iostream>
namespace Foo // Foo is defined in the global scope
{
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;
}
尽管标识符 g_x 现已被限制在命名空间 Foo 的作用域内,但该名称仍可通过 Foo::g_x 全局访问,且 g_x 仍属于全局变量。
关键要点
在命名空间内部声明的变量同样属于全局变量。
最佳实践
建议将全局变量定义在命名空间内部,而非全局命名空间中。
全局变量具有静态存储期
全局变量在程序启动时创建(在 main() 开始执行之前),并在程序结束时销毁。这称为静态存储期static duration。具有静态作用域的变量有时被称为静态变量static variables。
全局变量的命名
按惯例,部分开发者会在全局变量标识符前添加前缀“g”或“g_”,以表明其全局性。该前缀具有多重作用:
• 避免与全局命名空间中其他标识符发生命名冲突
• 防止无意间造成名称遮蔽(本主题将在第7.5节——变量遮蔽(名称隐藏)中深入探讨)
它有助于表明带前缀的变量在函数作用域之外仍保持存在,因此对它们所做的任何修改也将持续生效。
在用户定义命名空间内定义的全局变量通常省略前缀(因上述前两点在此场景不适用,且前缀命名空间名本身即暗示变量全局性)。但若为突出强调第三点而保留前缀,亦无不可。
最佳实践
建议为全局变量(尤其是定义在全局命名空间中的变量)命名时使用“g”或“g_”前缀,以便与局部变量和函数形参区分开来。
作者注
我们有时会收到读者反馈,询问诸如 g_ 之类的前缀是否可行,因为他们被告知前缀属于匈牙利命名法,而“匈牙利命名法很糟糕”。
反对匈牙利命名法的观点主要源于其将变量类型嵌入变量名的做法,例如nAge中n代表int类型。这种写法在现代C++中已无太大价值。
然而,使用前缀(典型形式为g/g_、s/s_和m/m_)来表示变量的作用域或存储期确实具有实用价值,具体原因将在本节中阐述。
全局变量初始化
与默认未初始化的局部变量不同,具有静态存储期的变量默认采用零初始化。
非常量全局变量可选择性初始化:
int g_x; // no explicit initializer (zero-initialized by default)
int g_y {}; // value initialized (resulting in zero-initialization)
int g_z { 1 }; // list initialized with specific value
常量全局变量
与局部变量类似,全局变量也可以是常量。与所有常量一样,常量全局变量必须进行初始化。
#include <iostream>
const int g_x; // error: constant variables must be initialized
constexpr int g_w; // error: constexpr variables must be initialized
const int g_y { 1 }; // const global variable g_y, initialized with a value
constexpr int g_z { 2 }; // constexpr global variable g_z, initialized with a value
void doSomething()
{
// global variables can be seen and used everywhere in the file
std::cout << g_y << '\n';
std::cout << g_z << '\n';
}
int main()
{
doSomething();
// global variables can be seen and used everywhere in the file
std::cout << g_y << '\n';
std::cout << g_z << '\n';
return 0;
}
// g_y and g_z goes out of scope here

相关内容
我们在第7.10节中更详细地讨论了全局常量——在多个文件中共享全局常量(使用内联变量)。
关于(非常量)全局变量的警示
新手程序员常会倾向于大量使用全局变量,因为它们无需显式传递就能被所有需要它们的函数调用。然而,非常量全局变量通常应完全避免使用!我们将在后续的第7.8节——为何(非常量)全局变量是恶魔 中探讨原因。
快速摘要
// Non-constant global variables
int g_x; // defines non-initialized global variable (zero initialized by default)
int g_x {}; // defines explicitly value-initialized global variable
int g_x { 1 }; // defines explicitly initialized global variable
// Const global variables
const int g_y; // error: const variables must be initialized
const int g_y { 2 }; // defines initialized global const
// Constexpr global variables
constexpr int g_y; // error: constexpr variables must be initialized
constexpr int g_y { 3 }; // defines initialized global constexpr

浙公网安备 33010602011771号