static 关键字教程

在 C/C++ 中,static 是一个功能灵活的关键字,可修饰变量(局部变量、全局变量)和函数,核心作用是控制作用域、生命周期或链接属性,不同场景下用法和效果差异较大,需结合具体修饰对象理解。

一、修饰局部变量:延长生命周期至程序运行期

局部变量默认存储在栈区,生命周期随函数调用结束而销毁;用 static 修饰后,局部变量存储在全局数据区,生命周期延长至整个程序运行期间,且仅初始化一次。

语法与示例

#include <stdio.h>

void count() {
    // static局部变量:仅初始化1次,生命周期为程序运行期
    static int num = 0; 
    num++; // 每次调用函数,num值持续累加(不重置)
    printf("调用次数:%d\n", num);
}

int main() {
    count(); // 输出:调用次数:1
    count(); // 输出:调用次数:2
    count(); // 输出:调用次数:3
    return 0;
}

核心特性

  1. 生命周期延长:从“函数调用期”变为“程序运行期”,程序结束后才释放内存。
  2. 仅初始化一次:即使函数多次调用,static 局部变量的初始化语句(如 num = 0)仅执行一次。
  3. 作用域不变:仍局限于所在函数,外部无法直接访问(区别于全局变量)。

二、修饰全局变量:限制作用域为当前文件

全局变量默认存储在全局数据区,生命周期为程序运行期,且具有“外部链接属性”(可通过 extern 在其他文件中访问);用 static 修饰后,全局变量的链接属性变为“内部链接”,仅能在当前 .c/.cpp 文件中访问,其他文件无法引用。

语法与示例

假设有两个文件:file1.cfile2.c

file1.c(定义 static 全局变量)

// static全局变量:仅能在file1.c中访问
static int global_val = 100; 

void printGlobal() {
    printf("global_val: %d\n", global_val); // 合法:同一文件内可访问
}

file2.c(尝试访问 file1.c 的 static 全局变量)

#include <stdio.h>

// 错误:extern无法引用其他文件的static全局变量(链接属性不匹配)
extern int global_val; 

int main() {
    // 编译报错:undefined reference to 'global_val'
    printf("global_val: %d\n", global_val); 
    return 0;
}

核心特性

  1. 作用域限制:仅当前文件可见,避免不同文件中同名全局变量的冲突(“命名隔离”)。
  2. 生命周期不变:仍为程序运行期,初始化在程序启动时完成。
  3. 链接属性变化:从“外部链接”变为“内部链接”,extern 无法跨文件引用。

三、修饰函数:限制作用域为当前文件

函数默认具有“外部链接属性”,可通过 extern 在其他文件中调用;用 static 修饰后,函数变为“内部链接”,仅能在当前文件中被调用,其他文件无法引用该函数。

语法与示例

file1.c(定义 static 函数)

// static函数:仅能在file1.c中调用
static void internalFunc() { 
    printf("这是文件内部函数\n");
}

// 非static函数:可被其他文件调用
void callInternal() {
    internalFunc(); // 合法:同一文件内可调用static函数
}

file2.c(尝试调用 file1.c 的 static 函数)

#include <stdio.h>

// 错误:extern无法引用其他文件的static函数
extern void internalFunc(); 

int main() {
    // 编译报错:undefined reference to 'internalFunc'
    internalFunc(); 
    return 0;
}

核心特性

  1. 作用域限制:仅当前文件可见,避免不同文件中同名函数的冲突(常用于“文件内部工具函数”)。
  2. 链接属性变化:从“外部链接”变为“内部链接”,extern 无法跨文件调用。

四、C++ 中的额外用法:修饰类成员(静态成员)

C++ 中,static 可修饰类的成员变量和成员函数,称为“静态成员”,其核心特点是属于类本身,而非类的某个对象,所有对象共享静态成员。

1. 静态成员变量

  • 存储在全局数据区,生命周期为程序运行期,仅初始化一次(需在类外单独初始化)。
  • 所有对象共享该变量,修改一个对象的静态成员变量,会影响所有对象。

示例

#include <iostream>
using namespace std;

class Student {
public:
    // 静态成员变量:属于Student类,所有对象共享
    static int totalCount; 
    string name;

    Student(string n) : name(n) {
        totalCount++; // 每创建一个对象,总人数+1
    }
};

// 静态成员变量必须在类外初始化(类内仅声明)
int Student::totalCount = 0; 

int main() {
    Student s1("张三");
    Student s2("李四");
    // 两种访问方式:类名::静态变量 / 对象.静态变量
    cout << "总人数(类访问):" << Student::totalCount << endl; // 输出:2
    cout << "总人数(对象访问):" << s1.totalCount << endl;      // 输出:2
    return 0;
}

2. 静态成员函数

  • 属于类本身,不依赖于对象,没有 this 指针(无法访问类的非静态成员,仅能访问静态成员)。
  • 调用方式:类名::静态函数()对象.静态函数()(推荐前者,更清晰)。

示例

#include <iostream>
using namespace std;

class MathUtil {
public:
    // 静态成员函数:无this指针,仅能访问静态成员
    static int add(int a, int b) { 
        return a + b;
    }

    // 错误:静态函数无法访问非静态成员(无this指针)
    // static int getVal() { return val; } 
    int val; // 非静态成员
};

int main() {
    // 无需创建对象,直接通过类名调用静态函数
    cout << "3 + 5 = " << MathUtil::add(3, 5) << endl; // 输出:8
    return 0;
}

五、static 关键字核心特性总结

修饰对象 核心作用 生命周期 作用域/链接属性
局部变量 延长生命周期至程序运行期,仅初始化一次 程序运行期 所在函数内(不变)
全局变量 限制作用域为当前文件 程序运行期(不变) 从“外部链接”变为“内部链接”
函数
C++类成员变量 属于类,所有对象共享,需类外初始化 程序运行期 类内可见,可通过类名/对象访问
C++类成员函数 属于类,无this指针,仅访问静态成员 类内可见,可通过类名/对象调用

六、注意事项

  1. 初始化位置
    • C/C++ 中,static 全局变量、静态局部变量的初始化在程序启动时(主函数执行前)完成,且仅一次。
    • C++ 类的静态成员变量必须在类外单独初始化(类内仅声明,初始化格式:类型 类名::变量名 = 初始值)。
  2. 线程安全
    • 静态变量(尤其是静态局部变量)在多线程环境下可能存在“初始化竞争”问题(C++11后静态局部变量初始化已线程安全,但修改仍需加锁)。
  3. 避免滥用
    • 静态局部变量会延长内存占用,长期运行的程序需注意内存泄漏风险。
    • 静态全局变量/函数会增加文件间耦合,非必要不使用(优先用“局部变量+函数参数”传递数据)。

七、常见面试考点

  1. static 局部变量与普通局部变量的区别:生命周期、初始化次数、存储区域。
  2. static 全局变量与普通全局变量的区别:链接属性(跨文件访问能力)。
  3. C++ 静态成员函数为什么不能访问非静态成员:无 this 指针,无法定位到具体对象的非静态成员。
  4. static 关键字的作用总结:按“修饰局部变量、全局变量、函数、类成员”分类回答。

0 条评论

目前还没有评论...