CC++ & Algorithm

变量的作用域

中等4
语言版本:C++
概述:了解变量“能活”在哪些地方:全局变量全校都能访问,局部变量只在自己的函数或代码块中可见。

变量能活多久?作用域让你分清“全校可见”和“自己独享”

你有没有注意到,在学校里,有些通知贴在公告栏上,全校师生都能看到;而你自己书桌上的小纸条,只有你一个人能看。在C++程序中,变量也有类似的“有效范围”——有的变量整个程序都能用,有的只能在一个函数或者一个小代码块里存活。这个“有效范围”就叫做作用域。搞懂作用域,能帮你写出更干净、不容易出错的代码,避免变量“串门”带来的混乱。

全局变量:公告栏上的通知

定义在所有函数外面的变量叫做全局变量。它就像贴在公告栏上的通知,程序里任何地方(任何函数、任何代码块)都可以读写它。

#include <iostream>
using namespace std;

int global_score = 100;  // 全局变量:整个程序都能看到

void show_global() {
    cout << "在 show_global 函数里,global_score = " << global_score << endl;
}

int main() {
    cout << "在 main 函数里,global_score = " << global_score << endl;
    global_score = 200;   // 改了全局变量
    show_global();        // 输出 200
    return 0;
}

生活例子:全班同学的零花钱总额,如果存成全局变量,全班每个同学都能随时查看和修改。好处是方便,坏处是有一个人乱改,大家都会受影响。

注意事项

  • 全局变量可以在任何地方被修改,如果多个函数都去改它,程序逻辑会变得混乱,调试时很难定位是谁把它改错了。
  • 尽量少用全局变量,只在确实需要多个函数共享同一个数据时才使用,比如程序配置、计数器等。

局部变量:书包里的东西

定义在函数内部代码块(一对花括号 {}) 内部的变量叫做局部变量。它就像你书包里的文具,只有你自己能用,别人碰不到。一旦离开它所在的花括号,这个变量就被系统“回收”了,再也无法使用。

#include <iostream>
using namespace std;

void eat_snack() {
    int snack_count = 5;  // 局部变量:只在 eat_snack 函数里有效
    cout << "书包里有 " << snack_count << " 包零食" << endl;
}

int main() {
    // cout << snack_count;   // 错误! main 里看不到 snack_count
    eat_snack();               // 正常输出 5
    return 0;
}

生活例子:你在自己的床上写作业,把笔放在床头柜上。出了你的房间,你就找不到那支笔了。每个函数就像你的房间,里面定义的局部变量只有房间里面的人能用。

重要特性

  • 不同函数可以定义同名的局部变量,它们互不干扰,就像你在自己房间放了一本《数学书》,我在自己房间也放了一本《数学书》,各看各的。
  • 局部变量在函数每次被调用时都会重新创建,函数返回时就销毁。

块作用域:书包里的暗格

花括号 {} 本身就能定义一个“代码块”。在代码块内部定义的变量,只在这个块内有效。块可以嵌套,例如在 ifforwhile 的循环体里定义的变量,只在那次循环中有效。

#include <iostream>
using namespace std;

int main() {
    int total = 0;          // main函数的局部变量
    for (int i = 0; i < 3; i++) {   // i 只在 for 循环的代码块内有效
        int temp = i * 10;          // temp 也只在 for 循环的 {} 内有效
        total += temp;
        cout << "循环内: i=" << i << ", temp=" << temp << endl;
    }
    // cout << i;     // 错误!i 离开了 for 循环就没了
    // cout << temp;  // 错误!temp 也消失了
    cout << "total = " << total << endl;
    return 0;
}

你还可以手动创建代码块,用来隔离一些临时变量:

int main() {
    int a = 1;
    {
        int b = 2;   // b 只在这个花括号里有效
        a = a + b;
        cout << "块内: a=" << a << ", b=" << b << endl;
    }
    // cout << b;    // 错误!b 已销毁
    cout << "块外: a=" << a << endl;
    return 0;
}

生活例子:就像你书包里还有一个小暗格,暗格里放着你的私房钱。打开书包能看到暗格里的钱,但合上暗格(离开代码块),别人就看不到了。

同名变量怎么办?就近原则!

如果全局变量和局部变量同名,会发生“屏蔽”现象:在函数或代码块内部,局部变量会“盖住”全局变量,就像你的左手挡住右手——程序会优先使用离得最近的变量。

#include <iostream>
using namespace std;

int candy = 10;  // 全局变量

void eat() {
    int candy = 3;   // 局部变量,名字和全局的一样
    cout << "局部糖果: " << candy << endl;   // 输出 3
    // 如果想访问全局的 candy,需要用 ::candy(作用域运算符)
    cout << "全局糖果: " << ::candy << endl; // 输出 10
}

int main() {
    eat();
    cout << "主函数中 candy = " << candy << endl;   // 输出 10(全局的)
    return 0;
}

新手容易犯的错误

  • 在函数内部修改了同名的局部变量,以为改了全局变量,结果全局变量纹丝不动。
  • 忘记用 ::(作用域解析运算符)来访问同名的全局变量,导致逻辑错误。

变量的生命周期:从出生到消失

  • 全局变量:在程序启动时就创建,直到程序结束才销毁。它一直活着。
  • 局部变量:在进入它所在的函数或代码块时创建,离开时就销毁。每次调用函数都会重新创建。
  • 代码块内的变量:和局部变量一样,块开始创建,块结束销毁。

理解生命周期能帮助你避免“使用已经死掉的变量”的错误。

常见错误与避坑指南

  1. 在函数外部试图使用局部变量

    void test() {
        int x = 5;
    }
    int main() {
        // cout << x;  // 编译错误:x 不存在于 main 的作用域
    }
    

    解决:把需要的值通过函数返回值或参数传递出来。

  2. 滥用全局变量导致变量混乱

    int count = 0;
    void inc() { count++; }
    void dec() { count--; }
    // 在多处调用 inc 和 dec 后,count 的值很难追踪
    

    建议:能用局部变量就用局部变量,只在真正需要共享时用全局变量。

  3. 在 if 或 for 外面引用循环内的变量

    for (int i = 0; i < 5; i++) { /* ... */ }
    // cout << i;   // 错误!i 只存活在 for 循环内
    

    解决:把循环需要保留的变量定义在循环外面。

完整示例:考试分数与零花钱管理

下面的程序综合展示了全局变量、局部变量和代码块变量的用法。你可以看到它们如何相互影响。

#include <iostream>
using namespace std;

int allowance = 50;  // 全局变量:全家共用的零花钱基数

void show_info() {
    int day = 1;             // 局部变量:只在这个函数里有效
    cout << "第 " << day << " 天:" << endl;
    cout << "当前零花钱基数: " << allowance << endl;
    day++;   // 函数返回后 day 消失,所以每次调用 day 都是1
}

int main() {
    show_info();   // 输出“第 1 天:当前零花钱基数: 50”

    // 代码块:用来模拟一次考试
    {
        int exam_score = 95;       // 块内局部变量,只在花括号里有效
        cout << "考试成绩: " << exam_score << endl;
        if (exam_score >= 90) {
            allowance += 10;       // 修改全局变量
        }
        // exam_score 在这个块结束后销毁
    }
    // cout << exam_score;  // 错误!exam_score 已失效

    show_info();   // 输出“第 1 天:当前零花钱基数: 60”(全局变量被加了10)

    // 局部的 allowance 屏蔽全局变量
    int allowance = 200;           // 局部变量,与全局同名
    cout << "在 main 函数里的 allowance: " << allowance << endl;     // 输出 200
    cout << "全局的 allowance: " << ::allowance << endl;             // 输出 60

    return 0;
}

运行结果:

第 1 天:当前零花钱基数: 50
考试成绩: 95
第 1 天:当前零花钱基数: 60
在 main 函数里的 allowance: 200
全局的 allowance: 60

从这个例子你可以看到:

  • 全局变量 allowance 被多个地方读写,容易引起连锁变化。
  • 局部变量 day 每次调用 show_info 都是新的,从1开始。
  • 代码块内的 exam_score 只在块内有效,出块就失效。
  • 同名的局部变量屏蔽了全局变量,但通过 ::allowance 还能访问到全局的。

相关知识点指引

掌握了变量的作用域,你就可以更深入地学习:

  • 函数参数的作用域:参数也是局部变量,只在函数内部有效。
  • 静态局部变量(static:用 static 修饰的局部变量,生命周期延长到整个程序,但可见范围仍然在函数内——就像给局部变量加了一个“永久保存”的保险箱。
  • 命名空间(namespace:可以用来区分不同模块中的同名变量,类似于“班级中的张三”和“年级中的张三”。
  • 递归函数中的作用域:每次递归调用都会创建新的局部变量副本,互不干扰。

试着用作用域的知识去分析你写过的程序,看看哪些变量可以缩小范围,从而让代码更清晰、更安全。记住一个原则:变量的作用域越小越好,除非有必要共享。 这样你的代码会少很多“抓狂”的调试时刻。

例题精讲

1单选题

在C++中,以下关于变量作用域的叙述正确的是?

A在函数内部定义的变量可以在函数外部访问
B全局变量可以在任何函数内直接访问
C不同函数内可以定义同名变量,但会有冲突
D局部变量在程序运行期间一直存在
2判断题

在C++中,如果在函数内部定义了一个与全局变量同名的局部变量,则在函数内部访问该名称时,访问的是局部变量。

3填空题
以下代码中,函数fun()内需要输出全局变量a的值。请填空:
int a = 10;
void fun() {
    int a = 20;
    cout << ___ << endl;
}
4单选题

关于函数参数的作用域,下列说法正确的是?

A函数参数的作用域是整个程序
B函数参数的作用域是定义它的函数内部
C函数参数可以同时在多个函数中使用
D函数参数在函数外部也可以访问
5判断题

在C++中,在for循环的初始化部分定义的变量(如:for(int i=0; i<10; i++))作用域仅限于该循环体内部。