C/C++ static 的用法详解

C/C++ static 的用法详解

在 C/C++中,static 是一个多用途的关键字,用法因语境(全局、局部、类/结构体、函数等)而异,核心作用是控制变量/函数的生命周期、可见性或存储方式

在C 语言中的 static

1. 文件作用域:静态全局变量

  • 定义:在全局变量前加 static,其作用域会被限制在当前编译单元(.c 文件),在其他文件中不可见。
  • 生命周期:与程序同生共死(生命周期相同)。
  • 特性
    • 避免全局变量命名冲突(其他文件可定义同名变量,互不干扰)。
    • 仅在当前文件内可访问,实现“文件内私有”。

// file1.c
// 静态全局变量,仅 file1.c 可见。
static int g_val = 10; 

// file2.c
// 与 file1.c 的 g_val 不冲突,各自独立。
int g_val = 20; 

2. 函数作用域:静态局部变量

  • 定义:在函数内部定义的变量前加 static,作用域仍限于函数内部。
  • 生命周期:函数首次被调用时初始化此静态变量且仅初始化这一次,但可以被修改(非const变量),其生命周期延长至整个程序结束。
  • 特性
    • 对于函数内定义的静态局部变量不会因为函数的退出而销毁且可以保持其最新状态到程序结束。
    • 限定此类静态变量的作用域仅在定义它的函数内部。

#include <stdio.h>

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

int main() 
{
    // 输出 1
    printf("%d ", count()); 

    // 输出 2
    printf("%d ", count());  

    // 输出 3
    printf("%d ", count());  
    return 0;
}

3. 文件作用域:静态函数

  • 定义:在函数前加 static,作用域限制在当前编译单元(.c 文件),其他文件无法调用。
  • 特性
    • 可以避免函数命名冲突(在其他文件可定义同名函数)。
    • 将函数的作用域限定在当前编译单元,可以隐藏内部实现,实现私有化的效果。

// file1.c
static void helper()
{  
    // 静态函数,仅 file1.c 内可调用
    printf("Helper function\n");
}

void public_func() {
    helper();  // 合法:同一文件内可调用
}

// file2.c
// 声明外部函数
void public_func();  

// 错误:无法声明其他文件的静态函数
// void helper();  
int main() 
{
    // 合法:调用 public_func
    public_func(); 

    // 错误:无法访问 file1.c 的静态函数
    // helper();   
    return 0;
}

C++ 中扩展的 static 用法(兼容 C,新增类相关用法)

1. 类作用域:静态成员变量

  • 定义:在类的成员变量前加 static,属于整个类而非某个对象,此类的所有对象共享此静态成员。
  • 特性
    • 必须在类外初始化(类内仅声明),初始化时不加 static
    • 可通过 类名::变量名对象.变量名 访问(推荐前者)。
    • 生命周期与程序一致,不依赖对象创建。

#include 
<iostream>

class MyClass 
{
public:
    static int _val;  // 类内声明静态成员变量
};

// 类外初始化(必须!否则链接错误)
int MyClass::_val = 100;

int main() 
{
    MyClass obj1, obj2;
    obj1._val = 200;

    // 输出 200(所有对象共享)
    std::cout << obj2._val << std::endl;  

    // 输出 200(推荐用法)
    std::cout << MyClass::_val << std::endl; 
    return 0;
}

2. 类作用域:静态成员函数

  • 定义:在类的成员函数前加 static,属于类本身,不依赖对象即可调用。
  • 特性
    • this 指针,无法访问非静态成员(变量/函数),只能访问静态成员。
    • 可通过 类名::函数名对象.函数名 调用(推荐前者)。
    • 不能被 virtual 修饰(无多态特性)。

#include 
<iostream>

class MathUtil 
{
public:
    static int Add(int a, int b) 
    { 
        // 静态成员函数
        return a + b;
    }

    // 静态成员变量
    static double _pi;  
};

// 初始化静态成员变量(部分常量可以在类内部直接初始化)
double MathUtil::_pi = 3.14; 

int main() 
{
    // 无需创建对象,直接通过类名调用
    // 输出 5
    std::cout << MathUtil::Add(2, 3) << std::endl; 
    return 0;
}

3.(类/结构体)成员函数作用域:局部静态变量被此(类/结构体)的所有对象共享

  • 在 C++ 中,类的静态成员函数或非静态成员函数内的静态局部变量,与普通函数的静态局部变量特性一致,仅初始化一次且生命周期与程序一致。

class Counter 
{
public:
    int GetCount() 
    {
     // 静态局部变量,所有对象共享
     // 此处要特别注意,容易因为并行计算踩坑。
        static int cnt = 0; 
        cnt++;
        return cnt;
    }
};

int main() {
    Counter c1, c2;
    // 输出 1 2
    std::cout << c1.GetCount() << " " << c2.GetCount() << std::endl;  
    return 0;
}

4. 模版类:静态成员

类模板的静态成员变量/函数,会为每个模板实例化类型生成独立的副本(而非所有实例共享)。

  • 示例:
#include 
<iostream>

template <typename T>
class TempClass 
{
public:
    // 静态模板成员变量
    static int _val; 
};

// 为每个模板类型初始化
template <typename T>
int TempClass
<T>::_val = 0;

int main() 
{
    TempClass
<int>::_val = 10;
    TempClass
<double>::_val = 20;
     // 输出  10 20
    std::cout << TempClass<int>::_val << " " << TempClass<double>::_val << std::endl; 
    return 0;
}

static 的核心特性总结

用法场景 可见性(作用域) 生命周期 核心作用
静态全局变量(C/C++) 仅限当前 .c/.cpp 文件 程序启动至结束 避免全局变量冲突,实现文件内私有
静态局部变量(C/C++) 仅限函数内部 首次初始化后至程序结束 保存函数调用间的状态(如计数器)
静态函数(C/C++) 仅限当前 .c/.cpp 文件 程序启动至结束 隐藏内部实现,避免函数命名冲突
静态成员变量(C++ 类) 类内可见,类外通过类名访问 程序启动至结束 所有对象共享数据,减少全局变量依赖
静态成员函数(C++ 类) 类内可见,类外通过类名访问 程序启动至结束 提供不依赖对象的工具函数,仅访问类的静态成员

常见误区与注意事项

  1. 静态变量的初始化顺序
    不同编译单元的静态变量初始化顺序未定义(可能导致依赖问题),应避免在静态变量初始化中依赖其他静态变量。

  2. 多线程安全性
    静态局部变量的初始化在 C++11 后是线程安全的(编译器保证仅初始化一次),但后续修改仍需手动加锁。

  3. 类的静态成员变量必须初始化
    类内仅声明,类外必须定义并初始化(否则链接错误),初始化格式为 类型 类名::变量名 = 值;

  4. 静态成员函数无 this 指针
    无法访问非静态成员,因为非静态成员依赖具体对象(this 指向的实例)。

  5. C++ 中 static 不影响访问权限
    静态成员的访问权限仍受 public/private 控制(如 private static 变量仅类内可访问)。

  6. C++ 中类的静态变量现成安全问题 类的静态成员变量和局部静态成员变量是被类的所有对象共享的,此处要特别注意线程安全,避免并行计算时出错。

此条目发表在C/C++分类目录。将固定链接加入收藏夹。