C++ 变量作用域
C++ 中的变量作用域
Section titled “C++ 中的变量作用域”作用域(Scope)定义了程序中声明的标识符(identifier,如变量或函数名)的可访问区域。理解作用域对于避免命名冲突和控制变量的生命周期至关重要。C++ 有几个层级的作用域:
- 块作用域(Block Scope)/局部作用域(Local Scope): 在一个块(block)(用
{}括起来的代码,例如函数内、循环或条件语句内)内部声明的变量。这些变量只从它们的声明点到块的末尾可访问。 - 函数参数作用域(Function Parameter Scope): 在函数定义中声明的参数,其作用域仅限于函数体。
- 命名空间作用域(Namespace Scope)(包括全局作用域): 在任何块、函数或类外部声明的标识符。全局变量(在任何命名空间外部的命名空间作用域中声明)在其声明后可在整个程序文件中访问(受链接规则影响)。
- 类作用域(Class Scope): 类成员的名称对类局部,通过对象或指针(
.,->)或作用域解析运算符(::用于静态成员)访问。
本节主要关注块作用域(局部变量)和命名空间作用域(全局变量)。
局部变量(块作用域)
Section titled “局部变量(块作用域)”在函数或块({ ... })内部声明的变量是局部变量。它们在进入块时存在,并在退出块时被销毁。它们只在该块内部可访问。
#include <iostream>
int main() { // main 函数块的开始 // 在 main 的作用域中声明的局部变量 int a = 10; int b = 20; int c = a + b;
std::cout << "Inside main: c = " << c << std::endl;
if (a < b) { // if 块的开始 // 'd' 是此 if 块的局部变量 int d = b - a; std::cout << "Inside if-block: d = " << d << std::endl; // 'a', 'b', 'c' 在此处可访问(外部作用域) std::cout << "Inside if-block: a = " << a << std::endl; } // if 块的结束,'d' 被销毁
// std::cout << d; // 错误:'d' 在此处不可访问(超出作用域)
return 0;} // main 函数块的结束,'a', 'b', 'c' 被销毁局部变量默认具有自动存储期(automatic storage duration)—— 它们的生命周期与其作用域绑定。
全局变量(命名空间作用域)
Section titled “全局变量(命名空间作用域)”在任何函数、类或块外部定义的变量位于命名空间作用域(namespace scope)。如果在任何命名空间外部定义,则它们位于全局命名空间作用域(global namespace scope)。全局变量具有静态存储期(static storage duration)—— 它们在程序的整个生命周期内都存在。
全局变量可以被同一文件中在其声明之后声明的任何函数访问(如果变量具有外部链接 external linkage - 默认情况下)。
虽然方便,但过度使用全局变量是不被鼓励的,因为这可能导致命名冲突,并使代码更难理解和维护(函数变得不那么自包含)。
#include <iostream>
// 全局变量声明和定义// 默认具有静态存储期和外部链接int global_var = 100;
void print_global() { // 访问全局变量 std::cout << "Inside print_global: global_var = " << global_var << std::endl;}
int main() { // 局部变量声明: int local_a = 10; int local_b = 20;
// 访问和修改全局变量 global_var = local_a + local_b; std::cout << "Inside main: global_var = " << global_var << std::endl; // 输出 30
print_global(); // 输出 30
return 0;}名称遮蔽(Name Shadowing)
Section titled “名称遮蔽(Name Shadowing)”如果在局部作用域中声明的变量与外部作用域(如全局变量)中的变量同名,则局部变量在其作用域内会遮蔽(shadow)外部变量。局部名称具有优先权。
#include <iostream>
// 全局变量声明:int g = 20;
int main() { // 声明同名局部变量 'g' int g = 10; // 这个 'g' 遮蔽了全局的 'g'
std::cout << "Local g = " << g << std::endl; // 访问局部变量(输出 10)
// 要访问全局 'g',请使用作用域解析运算符 :: std::cout << "Global g = " << ::g << std::endl; // 访问全局变量(输出 20)
return 0;}编译并执行上述代码,会产生以下输出:
Local g = 10Global g = 20虽然可能,但遮蔽会使代码变得混乱。通常最好使用不同的名称。
变量的初始化
Section titled “变量的初始化”初始化行为因存储期而异:
- 静态存储期(Static Storage Duration)(全局/命名空间/静态局部变量): 如果未显式初始化,变量会在任何其他代码执行之前自动被系统零初始化(zero-initialized)。(数值类型为 0,指针为
nullptr,等等)。 - 自动存储期(Automatic Storage Duration)(非静态局部变量): 如果未显式初始化,变量具有不确定值(indeterminate values,即垃圾值)。读取未初始化的局部变量会导致未定义行为(undefined behavior)。在使用局部变量之前务必初始化!
静态存储期变量的默认零初始化:
| 数据类型 | 默认初始化器(静态存储期) |
|---|---|
| int, float, double, etc. | 0 或 0.0 |
| char | \0 (空字符) |
| bool | false |
| pointer types | nullptr |
| class/struct types | 调用默认构造函数,如果适用则成员零初始化 |
最佳实践:始终在声明时显式初始化变量,以避免意外行为,特别是对于局部变量。
int local_count; // 坏:未初始化(不确定值)int global_count; // 好:零初始化为 0
int main() { int initialized_local = 0; // 好:显式初始化 // std::cout << local_count; // 未定义行为! std::cout << initialized_local; // 好:输出 0 std::cout << global_count; // 好:输出 0}