📄 一句话概括
在函数里给局部变量加 static,它就变成"长生不老"的局部变量:初始化只做一次,之后活到程序结束,每次调用函数都接着上次的值继续。最经典的用法就是累加器 / 计数器。
📝 本节和前面几节的关系
6.7.1 的 auto 变量"每次重生",这一节给出反面答案:想留住值,就加 static。这里第一次出现"作用域和生存期可以不一致"——名字还是只在函数里看得见(局部),命却活到程序结束(全局)。这正是 6.7 开头那句"两个维度别混"的正式兑现。
一、写法与机制
void counter() {
static int s = 0; // 只在第一次调用时执行这一句
s++;
cout << s << endl;
}
关键在"初始化只做一次":
- 第一次调用:给 s 分配空间并赋 0
- 第二次、第三次……:跳过初始化那一句(s 保留着上次的值)
- 程序结束才销毁
为什么? static 变量不住在栈上(那是 auto 的地盘),它住在静态存储区——这块区域从程序启动到结束一直存在,函数返回不会回收它。
二、实例 07:使用静态变量实现累加
#include <iostream>
using namespace std;
void accumulate(int x) {
static int total = 0; // 只初始化一次,之后一直活着
total += x;
cout << "本次加 " << x << ",累计 " << total << endl;
}
int main() {
accumulate(10); // 累计 10
accumulate(20); // 累计 30
accumulate(5); // 累计 35
return 0;
}
看到什么算成功:
本次加 10,累计 10
本次加 20,累计 30
本次加 5,累计 35
动手做这个对比实验(本节最重要的一步):把 static 删掉再跑,输出会变成:
本次加 10,累计 10
本次加 20,累计 20
本次加 5,累计 5
三行各自独立、互不相识——因为 total 每次都重生。加回 static,三行又串成一条链。这一个实验比任何解释都直观。
三、static 的三种用法(本章涉及前两种)
| 用法 | 位置 | 效果 |
|---|---|---|
| 局部 static | 函数体内 | 记住上次的值(本节主角);作用域仍局限在函数里 |
| 全局 static | 所有函数外面 | 全局变量只在本文件可见,别的 .cpp 看不见(防命名冲突) |
| 类的静态成员 | 类里面 | 学到"类"时才讲(表示"属于类、不属于某个对象") |
本节聚焦第一种——它最实用,也最容易让人惊讶:"这个函数的变量怎么记住上一次的?"
四、static 的实用场景(不只是玩具)
| 场景 | 用 static 解决 |
|---|---|
| 计数 | 函数被调用了多少次(本节实例) |
| 缓存 | 第一次算完存起来,后面直接返回,避免重复计算 |
| 一次性初始化 | 只做一次的准备动作(比如只读一次配置) |
| 生成唯一编号 | 每次调用发出一个递增的编号 |
五、常见误区
- "static 变量在函数外面也能用"——不能。作用域仍是局部的,外面看不见这个名字(这正是"作用域 ≠ 生存期"的第一个反例)。
- "static 变量每次调用都会重新赋 0"——反了。初始化只做一次,这是它和 auto 的分水岭。
- "static 变量天生线程安全"——单线程没问题;多线程时所有线程共享这一个变量,需要额外保护(后面章节的话题,先记住"共享"这个词)。
- "static 变量不用管内存"——它会自动管理,但从头活到尾:大量(或很大)的 static 变量会一直被占着,程序结束才释放。它不是免费的储物间。
- "初始化一定在程序启动时做"——准确说是第一次执行到那一句时初始化。如果那句在
if里面、第一次没走到,就等真正走到时才初始化。
自测三问(点开对答案)
1. 函数里的 static 变量有什么特别?——初始化只做一次,之后活到程序结束,值在多次调用间保留。 2. static 变量的作用域和生存期分别是什么?——作用域局部(函数内可见),生存期全局(程序结束才销毁)。 3. 去掉 `static` 后累加器的输出会怎样?——每次都从 0 开始,三行互相独立。- ☐ 6.7.3 register变量(下一节)
相关笔记
- auto变量 —— 每次重生的对照面
- 变量的存储类别 —— 四兄弟全景
- 变量作用域 —— 作用域与生存期的分工
- 🔷 C++基础 · 索引 —— 全课路线图