🔷 第六章 函数
📄 一句话概括

在函数里给局部变量加 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 解决
计数 函数被调用了多少次(本节实例)
缓存 第一次算完存起来,后面直接返回,避免重复计算
一次性初始化 只做一次的准备动作(比如只读一次配置)
生成唯一编号 每次调用发出一个递增的编号

五、常见误区

  1. "static 变量在函数外面也能用"——不能。作用域仍是局部的,外面看不见这个名字(这正是"作用域 ≠ 生存期"的第一个反例)。
  2. "static 变量每次调用都会重新赋 0"——反了。初始化只做一次,这是它和 auto 的分水岭。
  3. "static 变量天生线程安全"——单线程没问题;多线程时所有线程共享这一个变量,需要额外保护(后面章节的话题,先记住"共享"这个词)。
  4. "static 变量不用管内存"——它会自动管理,但从头活到尾:大量(或很大)的 static 变量会一直被占着,程序结束才释放。它不是免费的储物间。
  5. "初始化一定在程序启动时做"——准确说是第一次执行到那一句时初始化。如果那句在 if 里面、第一次没走到,就等真正走到时才初始化。
自测三问(点开对答案) 1. 函数里的 static 变量有什么特别?——初始化只做一次,之后活到程序结束,值在多次调用间保留。 2. static 变量的作用域和生存期分别是什么?——作用域局部(函数内可见),生存期全局(程序结束才销毁)。 3. 去掉 `static` 后累加器的输出会怎样?——每次都从 0 开始,三行互相独立。

相关笔记