C++存储类
存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。
下面列出 C++ 程序中可用的存储类:
auto:这是默认的存储类说明符,通常可以省略不写。auto 指定的变量具有自动存储期,即它们的生命周期仅限于定义它们的块。auto 变量通常在栈上分配。register:用于建议编译器将变量存储在CPU寄存器中以提高访问速度。在 C++11 及以后的版本中,register 已经是一个废弃的特性,不再具有实际作用。static:用于定义具有静态存储期的变量或函数,它们的生命周期贯穿整个程序的运行期。在函数内部,static变量的值在函数调用之间保持不变。在文件内部或全局作用域,static变量具有内部链接,只能在定义它们的文件中访问。extern:用于声明具有外部链接的变量或函数,它们可以在多个文件之间共享。==默认情况下,全局变量和函数具有extern存储类。==在一个文件中使用extern声明另一个文件中定义的全局变量或函数,可以实现跨文件共享。
从 C++ 17 开始,auto 关键字不再是 C++ 存储类说明符,且 register 关键字被弃用。
从 C++11 开始,register 已经失去了原有的作用,而 mutable 和 thread_local 则是新引入的特性,用于解决特定的编程问题。
一.static 存储类
在 C++ 中,static 是一个多功能的关键字,作为存储类说明符时,它主要用于控制变量或函数的存储期(生命周期)、作用域和链接性,核心作用是改变实体的默认行为。下面从不同场景详细解释:
1)static 修饰变量:3 种核心场景
1. 局部静态变量(函数内的 static 变量)
- 存储期:==从程序启动到程序结束(与全局变量生命周期相同),而非随函数调用创建、随函数返回销毁。==
- 作用域:==仅在定义它的函数 / 代码块内可见(局部作用域)==。
- 初始化:仅在第一次进入函数时初始化一次,后续调用不再重新初始化(默认零初始化,若显式初始化则按指定值初始化)。
void count() {
static int num = 0; // 局部静态变量,仅初始化一次
num++;
std::cout << num << " "; // 每次调用递增
}
int main() {
count(); // 输出 1
count(); // 输出 2
count(); // 输出 3
return 0;
}
2. 全局静态变量(文件内的 static 变量)
- 存储期:整个程序运行期间(同全局变量)。
- 作用域:仅在定义它的当前源文件(
.cpp) 内可见(限制了链接性,从 “外部链接” 变为 “内部链接”)。 - 初始化:程序启动时零初始化(未显式初始化时)。
// 文件 a.cpp
static int global_static = 10; // 仅在 a.cpp 内可见
// 文件 b.cpp
extern int global_static; // 错误!无法访问 a.cpp 中的 static 全局变量
3. 类的静态成员变量(static 成员)
- 存储期:属于类本身,而非类的某个实例,生命周期同程序。
- 作用域:类的作用域内,可通过
类名::成员名或对象访问(但需先在类外定义)。 - 初始化:必须在类外单独定义(且只能定义一次),未显式初始化则零初始化。
class MyClass {
public:
static int count; // 类内声明
};
int MyClass::count = 0; // 类外定义(必须)
int main() {
MyClass::count++; // 直接通过类名访问
MyClass obj;
obj.count++; // 也可通过对象访问
std::cout << MyClass::count; // 输出 2
return 0;
}
2)static 修饰函数:限制函数的链接性
- 作用:
static修饰的函数(全局函数或类的非成员函数)仅在当前源文件内可见(内部链接),其他文件无法通过extern调用。 - 用途:避免不同文件中同名函数的冲突,实现 “文件内私有函数”。
// 文件 utils.cpp
static void helper() { // 仅在 utils.cpp 内可见
// ...
}
void public_func() {
helper(); // 可调用
}
// 文件 main.cpp
extern void helper(); // 错误!无法访问 utils.cpp 中的 static 函数
二. extern 存储类
1)extern 的核心功能:声明外部实体
C++ 中,实体(变量 / 函数)的使用需要经过 “声明” 和 “定义” 两个步骤:
- 定义(definition):为实体分配内存,指定初始值(变量)或实现(函数),一个实体只能定义一次。
- 声明(declaration):告诉编译器实体的类型和名字,不分配内存,可多次声明。
extern的作用就是显式声明一个 “已在其他地方定义” 的实体,让当前文件可以使用它。
2)extern 修饰变量:跨文件共享变量
当需要在多个源文件(.cpp)中使用同一个全局变量时,extern 是标准用法:
- 在一个源文件中定义变量(仅一次):
无需extern,直接定义(分配内存)。 - 在其他源文件中用
extern声明变量(可多次):
告诉编译器 “该变量已在别处定义,这里直接用即可”。注意:// 文件 a.cpp(定义变量)
int global_var = 100; // 定义全局变量,分配内存并初始化
// 文件 b.cpp(声明并使用变量)
extern int global_var; // 声明:该变量在其他地方定义
void print() {
std::cout << global_var; // 正确:使用 a.cpp 中定义的变量
}
// 文件 main.cpp(声明并使用变量)
extern int global_var; // 再次声明
int main() {
global_var = 200; // 修改变量
print(); // 输出 200(跨文件生效)
return 0;
}
extern声明变量时不能初始化(初始化属于定义行为),例如extern int x = 5;是错误的,这会被编译器视为 “定义”,可能导致重复定义错误。- 如果变量仅在单个文件中使用,建议用
static修饰(限制为内部链接),避免全局污染。
3)extern 修饰函数:跨文件共享函数
函数默认具有外部链接性(即可以被其他文件访问),因此通常不需要 extern 修饰。但 extern 可以显式声明 “该函数在其他文件中定义”,增强代码可读性。// 文件 func.cpp(定义函数)
void add(int a, int b) { // 函数默认外部链接
return a + b;
}
// 文件 main.cpp(声明并使用函数)
extern void add(int a, int b); // 显式声明(可选,不写extern也可)
int main() {
add(2, 3); // 正确:调用 func.cpp 中定义的函数
return 0;
}
说明:
- 函数声明时,
extern可以省略(因为函数默认外部链接),但加上extern更清晰地表明 “该函数定义在别处”。 - 如果函数用
static修饰(内部链接),则不能用extern在其他文件中声明(会导致链接错误)。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 宇宙尽头的森林!