liangbm3's blog

Back

这里之所以特地写一篇文章记录一下 C++ 中静态局部变量的应用场景,是因为静态局部变量是一种非常特殊的变量:静态局部变量的生命周期是整个程序的运行期间,它在第一次调用所在的函数时初始化,并且到程序结束时才会被销毁。它的作用域仅限于定义它的函数或代码块内部,和普通的局部变量相同。

1. 函数调用计数器#

静态局部变量的一个经典应用就是函数调用计数器,由于变量在函数调用结束后不会被销毁,我们可以用它来轻松记录一个函数被调用了多少次。这在调试的时候非常有用。示例:

#include <iostream>

void someFunction() 
{
    static int callCount = 0; // 仅在第一次调用时初始化为 0
    callCount++;
    std::cout << "该函数已被调用 " << callCount << " 次。" << std::endl;
}

int main() 
{
    someFunction(); // 输出: 该函数已被调用 1 次。
    someFunction(); // 输出: 该函数已被调用 2 次。
    someFunction(); // 输出: 该函数已被调用 3 次。
    return 0;
}
cpp

在调试的过程中,如果我们不是想知道一个函数被调用了多少次,而是想知道一个函数是否被调用过。例如我们想在一个循环调用的函数中打印一条一次性的进入消息,也可以使用静态局部变量:

void someFunction() 
{
    static bool first_run = true;
    if (first_run) 
    {
        std::cout << "Debug: Performing one-time setup inside someFunction().\n";
        // 在这里可以放置任何你只想执行一次的调试代码或检查
        first_run = false;
    }

    // ... 函数的其余逻辑 ...
}
cpp

2. 实现缓存#

静态局部变量还可以用来实现缓存,当一个函数的计算成本很高时,并且对于相同的输入总是产生相同的输出时,可以使用静态局部变量来缓存计算结果,避免重复计算。示例:

#include <iostream>
#include <map>
#include <thread>
#include <chrono>

// 模拟一个耗时的计算
int expensiveComputation(int input) 
{
    static std::map<int, int> cache; // 静态缓存

    // 检查结果是否已在缓存中
    if (cache.count(input)) 
    {
        std::cout << "从缓存中命中: " << input << std::endl;
        return cache[input];
    }

    std::cout << "正在计算: " << input << std::endl;
    // 模拟耗时操作
    std::this_thread::sleep_for(std::chrono::seconds(1));
    int result = input * input; // 假设这是复杂计算
    cache[input] = result;
    return result;
}

int main() 
{
    expensiveComputation(5); // 第一次调用,会计算
    expensiveComputation(10); // 第一次调用,会计算
    expensiveComputation(5);  // 第二次调用,直接从缓存返回
    return 0;
}
cpp

3. 在递归中保持状态#

有时候我们在递归的过程中,需要保持某些状态,除了使用全局变量的方法,还可以使用静态局部变量,例如:

#include <stdio.h>

void recursiveSum(int n) 
{
    static int sum = 0;
    if (n == 0) {
        printf("Total sum: %d\n", sum);
        return;
    }
    sum += n;
    recursiveSum(n - 1);
}

int main() 
{
    recursiveSum(5); // 输出 15
    return 0;
}
cpp

4. 生成唯一的 ID#

在有一些场景中,我们需要递增生成一些编号作为 ID,这时候就可以使用静态局部变量,例如:

#include <stdio.h>

int generateID() 
{
    static int id = 0;
    return ++id;
}

int main() 
{
    printf("%d\n", generateID());
    printf("%d\n", generateID());
    return 0;
}
cpp

这个函数每调用一次,就会返回一个递增的唯一 ID。

5. 有限状态机#

在有一些业务场景中,我们需要使用有限状态机来处理一些流程,例如解析器、协议处理等,这就需要一个函数能够记住它上一次执行的状态,这时候可以使用静态局部变量。例如一个处理数据流的函数,需要根据上一次处理的数据块来决定如何处理当前的数据块:

#include <stdio.h>

typedef enum 
{
    STATE_A,
    STATE_B,
    STATE_C
} State;

void process_char(char c) 
{
    static State currentState = STATE_A; // 维护当前状态

    switch (currentState) 
    {
        case STATE_A:
            printf("当前在状态 A, 处理字符: %c\n", c);
            if (c == '1') 
            {
                currentState = STATE_B; // 转换到状态 B
            }
            break;
        case STATE_B:
            printf("当前在状态 B, 处理字符: %c\n", c);
            if (c == '2') 
            {
                currentState = STATE_C; // 转换到状态 C
            }
            break;
        case STATE_C:
            printf("当前在状态 C, 处理字符: %c\n", c);
            if (c == '3') 
            {
                currentState = STATE_A; // 回到状态 A
            }
            break;
    }
}

int main() 
{
    process_char('a');
    process_char('1');
    process_char('b');
    process_char('2');
    process_char('c');
    return 0;
}
cpp

6. 实现单例模式#

在 C++ 实现单例模式时,为了实现线程安全,需要使用锁,同时还需要进行双重判空,我们可以利用 C++11 可以保证局部静态变量的初始化是线程安全的性质,来实现简洁的线程安全的单例模式:

class Singleton 
{
private:
    Singleton() 
    { 
        std::cout << "Singleton Instance Created\n"; 
    }
    ~Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

public:
    static Singleton& getInstance() 
    {
        static Singleton instance;  // C++11 保证线程安全
        return instance;
    }
};
cpp

7. 调试观察点#

这是静态变量一个极其强大的调试优势。因为静态局部变量的内存地址在整个程序运行期间是固定的,而普通局部变量存储在栈上,每次函数调用其地址都可能不同。在调试时如果我们怀疑一个变量的值被异常修改,但不知道是哪里的代码干的,调试技巧如下:

  1. 在调试器中运行程序,让函数至少执行一次,这样静态变量就被创建了。
  2. 在变量窗口中找到该静态变量,右键点击并选择“设置数据断点”或“在此地址设置断点”。
  3. 之后,只要这个变量的值发生改变,调试器就会立即中断,并指向导致修改的代码行,我们就可以知道这个变量的值是在哪里更改的了。
C++ 中静态局部变量的使用场景
https://liangbm3.site/blog/c-zhong-jing-tai-ju-bu-bian-liang-de-shi-yong-chang-jing
Author liangbm3
Published at 2025年7月5日
Comment seems to stuck. Try to refresh?✨