C++ 变量

变量(variable)是程序里存储数据的容器。每个变量都有名字、类型、值、地址。这一章我们看 C++ 变量的声明、初始化、作用域,以及现代 C++ 的两个重要新特性:auto 类型推导和 constexpr 编译期常量。

一、声明与初始化

C++ 声明变量要先告诉编译器类型(这是和 Python/JS 最大的不同)。声明方式有四种:

#include <iostream>
using namespace std;

int main() {
    // 1. 先声明后赋值
    int age;
    age = 20;

    // 2. 声明同时初始化(推荐)
    int score = 95;

    // 3. C++11 列表初始化(花括号,最安全)
    int count{0};
    double pi{3.14};

    // 4. 小括号初始化(构造函数风格)
    int year(2024);

    cout << age << " " << score << " " << count << endl;
    return 0;
}
// 输出: 20 95 0

四种初始化方式对比:

二、变量的作用域

作用域(scope)是变量"可见"的代码区域。C++ 有三种主要作用域:

#include <iostream>
using namespace std;

int globalVar = 100;   // 全局变量:函数外声明,程序结束才销毁

int main() {
    int localVar = 10; // 局部变量:函数内声明,函数结束销毁
    cout << globalVar << " " << localVar << endl;  // 100 10

    {   // 进入一个新"块作用域"
        int blockVar = 5;
        cout << blockVar << endl;  // 5
        // 这里能访问 globalVar, localVar, blockVar
    }
    // 离开块,blockVar 销毁
    // cout << blockVar << endl;   // 编译错误:blockVar 未声明

    return 0;
}

作用域越小越好——这是工程上的铁律。作用域越小,bug 越少,代码越好懂。

三、变量遮蔽(shadowing)

当内层作用域的变量和外层同名时,内层的会"遮蔽"外层的。这是常见 bug 来源,要小心:

#include <iostream>
using namespace std;

int x = 1;   // 全局 x

int main() {
    int x = 2;       // 局部 x 遮蔽了全局 x
    cout << x << endl;   // 2(局部 x)

    {
        int x = 3;   // 内层 x 遮蔽了外层 x
        cout << x << endl;   // 3
    }

    cout << x << endl;   // 2(局部 x)
    cout << ::x << endl; // 1(用 :: 显式访问全局 x)
    return 0;
}

编译器一般会警告变量遮蔽。建议开 -Wshadow 选项让它变成警告,避免不小心遮蔽。

四、static:静态变量

static 关键字用在局部变量上,意味着"只初始化一次,函数退出后依然保留"。常用于实现计数器、缓存:

#include <iostream>
using namespace std;

int counter() {
    static int count = 0;  // 静态局部变量:只初始化一次
    count++;
    return count;
}

int main() {
    cout << counter() << endl;  // 1
    cout << counter() << endl;  // 2
    cout << counter() << endl;  // 3
    cout << counter() << endl;  // 4
    return 0;
}

每次调用 counter(),count 不会被重新初始化为 0,而是保留上次的值。这种"函数内但生命周期是整个程序"的变量,适合延迟初始化(只在第一次调用时构造)。

五、const 与 constexpr 常量

常量是"不能被修改的变量"。C++ 有两种:

#include <iostream>
using namespace std;

const double PI = 3.14159;        // 运行时常量
constexpr int SIZE = 1024;        // 编译期常量(C++11)

int main() {
    const int MAX = 100;
    // MAX = 200;  // 编译错误:const 不能重新赋值

    int x = 10;
    const int& ref = x;   // const 引用:不能通过 ref 修改 x

    cout << PI << " " << SIZE << " " << MAX << endl;
    return 0;
}

规则:能用 constexpr 就用 constexpr。它能编译期计算,可以做更多优化。

六、auto:类型推导(C++11)

这是现代 C++ 最重要的特性之一。auto 让编译器根据初始值自动推导变量类型,大大简化代码:

#include <iostream>
#include <vector>
#include <string>
using namespace std;

int main() {
    // auto 让编译器根据初始值自动推导类型(C++11)
    auto i = 42;          // int
    auto d = 3.14;        // double
    auto s = "hi";        // const char*
    auto str = string("hi");  // string
    auto v = vector<int>{1, 2, 3};

    // 常见用法:简化迭代器/复杂类型书写
    vector<int> nums = {1, 2, 3, 4, 5};
    for (auto it = nums.begin(); it != nums.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    // 输出: 1 2 3 4 5
    return 0;
}

auto 的典型场景:

但要注意:auto 不该滥用。当类型对理解代码很重要时(比如函数返回值类型影响行为),还是写明白更好。

七、未初始化变量:坑!

C++ 局部变量默认不会初始化为 0!这是从 C 继承下来的"性能优先"设计。但容易出 bug:

#include <iostream>
using namespace std;

int main() {
    int x;                    // 未初始化:值未定义(可能是任意值)
    cout << x << endl;        // 危险!可能输出任意数字

    int y{};                  // 列表初始化:默认 0
    cout << y << endl;        // 0

    int z = int();            // 值初始化:0
    cout << z << endl;        // 0
    return 0;
}

建议:声明变量时一定给初始值。哪怕给个 0 也比不初始化强。更稳妥的写法是用列表初始化语法,它会自动赋 0。

小结

这一章你掌握了:变量声明的四种方式(推荐列表初始化)、三种作用域(全局/局部/块)、变量遮蔽、static 静态变量、const/constexpr 常量、auto 类型推导。下一篇我们看 C++ 的数据类型——这些变量到底能装什么样的数据。

← 上一篇 C++ 基础语法

下一篇 C++ 数据类型

✈️💬