一、作用域分类与同名变量遮蔽规则

1. 作用域的三大分类

(1)全局作用域

  • 定义:在所有函数、代码块之外定义的变量/函数,拥有全局作用域。
  • 核心特性:

    1. 在整个程序的所有函数、代码块中都可以访问(除非被同名局部变量遮蔽);
    2. 生命周期伴随程序全程,程序启动时创建,结束时销毁。
  • 示例:

    int x = 10; // 全局变量,全局作用域
    void func() {
        int x = 20; // 局部变量,遮蔽全局x
        cout << x; // 优先访问局部变量,输出20
    }
    int main() {
        func();
        cout << x; // 无同名局部变量,访问全局变量,输出10
        return 0;
    }

(2)局部作用域(函数作用域)

  • 定义:在函数内部定义的变量,拥有局部作用域,也叫函数作用域。
  • 核心特性:

    1. 仅在当前函数内部可访问,函数外部无法访问;
    2. 函数执行时创建,执行结束后销毁;
    3. 若与全局变量同名,局部变量会遮蔽全局变量,函数内默认访问局部变量。

(3)块作用域(复合语句作用域)

  • 定义:在{}包裹的代码块(复合语句)内定义的变量,拥有块作用域,是局部作用域的子集。
  • 核心特性:

    1. 仅在当前{}代码块内可访问,代码块结束后变量立即销毁;
    2. 若与外层代码块、全局变量同名,内层块变量会遮蔽外层变量,块内默认访问内层变量。
  • 示例:

    int a = 1; // 全局作用域
    void test() {
        int a = 2; // 函数局部作用域,遮蔽全局a
        {
            int a = 3; // 块作用域,遮蔽外层a
            a++; // 仅修改内层块的a,变为4,块结束后该变量销毁
        }
        a++; // 修改函数局部的a,变为3
        cout << a << " "; // 输出3
    }
    int main() {
        test();
        cout << a; // 输出全局的1
        return 0;
    }

2. 核心规则:就近原则(名字遮蔽)

当不同作用域存在同名变量时,编译器会优先选择当前所在的最小作用域内的变量,外层、全局的同名变量会被遮蔽,默认无法直接访问。

  • 访问优先级:内层块作用域 > 函数局部作用域 > 全局作用域。

二、全局作用域解析符 ::

1. 核心作用

当局部变量遮蔽了全局同名变量时,使用::变量名的格式,可以强制访问全局作用域内的变量,绕过就近原则的遮蔽效应。

2. 语法格式

::全局变量名

3. 示例

int value = 100; // 全局变量
void print1() {
    int value = 50; // 局部变量,遮蔽全局value
    cout << value << " "; // 优先访问局部变量,输出50
    cout << ::value << " "; // 强制访问全局变量,输出100
}
void print2() {
    cout << value << " "; // 无同名局部变量,直接访问全局,输出100
}
int main() {
    print1();
    print2();
    return 0;
}

三、函数的作用域与前向声明规则

1. 核心规则

C++中,函数必须先声明/定义,后调用。函数的作用域从它的声明/定义位置开始,到程序文件末尾结束。

  • 如果在调用函数时,编译器还没有看到该函数的声明或定义,会直接报编译错误:找不到函数

2. 示例

// 错误写法:调用在前,定义在后,无前置声明
int main() {
    greet(); // 调用点编译器未见过greet函数,编译报错
    return 0;
}
void greet() { // 函数定义在调用之后
    cout << "Hello!" << endl;
}

3. 两种正确写法

(1)函数定义放在调用之前

// 先定义,后调用,符合规则
void greet() {
    cout << "Hello!" << endl;
}
int main() {
    greet();
    return 0;
}

(2)函数前向声明(声明在前,定义在后)

// 先做前向声明,告知编译器函数的存在
void greet();
int main() {
    greet(); // 编译器已见过函数声明,调用合法
    return 0;
}
// 后完成函数定义
void greet() {
    cout << "Hello!" << endl;
}

四、考点

1. 易错点1:局部变量同名初始化的未定义行为

错误代码

int x = 10; // 全局变量
void f() {
    int x = x + 1; // 未定义行为,绝对不会输出11
    cout << x << endl;
}
int main() {
    f();
}

错误原因

在局部变量int x的定义语句中,=右侧的x会被编译器优先匹配当前正在定义的局部变量x,而非全局变量x。此时局部变量x还未完成初始化,属于未定义行为,程序结果不可预测。

2. 易错点2:作用域 ≠ 生命周期

  • 作用域:变量可以被访问的代码范围,是编译期的概念;
  • 生命周期:变量在内存中存在的时间,是运行期的概念。
  • 高频误区:块作用域内的变量在块结束后就被销毁,即使通过指针拿到了它的地址,也无法合法访问,会触发未定义行为。

3. 易错点3::: 仅能访问全局变量

:: 只能用于访问全局作用域的变量,无法用它访问外层函数、外层代码块中的同名局部变量。

4. 易错点4:全局变量的重复定义

全局作用域内,同名的全局变量只能定义一次,多次定义会触发编译错误;而不同局部作用域内的同名变量互不影响。


五、考点速记表

题目核心场景核心规则正确结论
全局与局部同名变量就近原则,局部优先函数内默认访问局部变量
嵌套块内同名变量内层块优先遮蔽外层块内仅修改内层变量,不影响外层
访问被遮蔽的全局变量::全局变量名强制访问::value可获取全局作用域的value
函数调用在前、定义在后必须先声明/定义后调用无前置声明会直接编译报错
局部变量定义时用自身初始化优先匹配当前局部变量,未初始化未定义行为,结果不可预测

标签: none

评论已关闭