C++ 静态变量与链接性
1. 三个核心概念
在 C++ 中,变量有 3 个属性:作用域(scope) 、链接性(linkage) 和 存储持续性(storage duration)。
存储持续性(存多久?)
| 类型 | 生命周期 | 说明 |
|---|---|---|
| 自动存储 | 从定义到代码块结束 | 普通局部变量,函数返回即销毁 |
| 静态存储 | 整个程序运行期间 | 程序启动时分配,结束时释放 |
| 动态存储 | 手动控制 | new/delete 管理 |
链接性(谁能访问?)
| 类型 | 可见范围 | 关键字 |
|---|---|---|
| 外部链接 | 所有文件 | 全局变量(无 static) |
| 内部链接 | 当前文件 | static |
| 无链接 | 当前代码块 | 局部变量 |
作用域(在哪能看见?)
- 全局作用域:文件任何位置都能访问
- 局部作用域:只能在定义它的代码块内访问
2. 三种静态变量
静态变量都是由静态存储持续性的------程序启动时分配,结束时释放。区别在于链接性不同。
2.1 外部链接性(全局变量)
cpp
int global = 1000; // 文件顶部,无 static
- 所有函数之外定义
- 所有文件 都能访问(用
extern声明) - 也称"外部变量"
在其他文件中使用:
cpp
// support.cpp
extern int global; // 声明引用 main.cpp 中的 global
extern只是声明 不是定义,它告诉编译器"这个变量在其他文件里定义了,链接时你去找到它"。
2.2 内部链接性(文件作用域)
cpp
static int one_file = 50; // 文件顶部,加 static
- 只在当前文件可见
- 其他文件即使有同名变量也不冲突
cpp
// main.cpp
static int one_file = 50;
// support.cpp
static int one_file = 888; // 完全不冲突,各自独立
这就是 static 在文件作用域的作用------限制链接性为内部,实现文件级别的封装。
2.3 无链接性(静态局部变量)
cpp
void demonstrate_static_local() {
static int count = 0; // 静态局部变量
int auto_var = 0; // 普通自动变量
count++;
auto_var++;
std::cout << "count = " << count << ", auto_var = " << auto_var << std::endl;
}
输出:
count = 1, auto_var = 1
count = 2, auto_var = 1
count = 3, auto_var = 1
static int count |
int auto_var |
|
|---|---|---|
| 初始化 | 只初始化一次 | 每次调用都初始化 |
| 函数返回后 | 保持值 | 销毁 |
| 经典用途 | 记录函数调用次数 | 普通临时计算 |
3. 三种静态变量速查表
| 声明位置 | 关键字 | 存储持续性 | 链接性 | 作用域 |
|---|---|---|---|---|
| 所有函数外 | 无 | 静态 | 外部 | 所有文件 |
| 所有函数外 | static |
静态 | 内部 | 当前文件 |
| 代码块内 | static |
静态 | 无 | 当前代码块 |
| 代码块内 | 无 | 自动 | 无 | 当前代码块 |
4. const 的特殊规则
cpp
const int MONTHS = 12; // 全局 const
在 C++ 中,全局 const 默认具有内部链接性,等价于:
cpp
static const int MONTHS = 12;
这意味着什么呢?
- 每个包含这个声明的
.cpp文件都有自己的一份MONTHS副本 - 不会产生重复定义错误
- 可以放心地把 const 放在头文件里
C 语言中 const 默认是外部链接,这是 C++ 和 C 的区别之一。
5. 用 :: 访问被隐藏的全局变量
当局部变量和全局变量同名时,局部变量会隐藏全局变量:
cpp
int global = 1000;
void func() {
int global = 999; // 局部变量隐藏全局变量
std::cout << global; // 输出 999(局部)
std::cout << ::global; // 输出 1000(全局,用 :: 访问)
}
:: 作用域解析运算符 ------当变量被隐藏时,用 ::变量名 强制访问全局版本。
6. 什么时候用什么?
| 场景 | 推荐方式 |
|---|---|
| 需要全局共享的数据 | 普通全局变量(外部链接) |
| 只想在当前文件使用的全局数据 | static 文件作用域(内部链接) |
| 需要跨函数保持值的计数器 | 函数内 static 局部变量 |
| 全局常量 | const(默认内部链接) |
替代 static 文件作用域 |
匿名名称空间(C++ 推荐,见名称空间篇) |
7. 总结
三种静态变量的本质差异在于链接性:
- 外部链接 (全局变量)--- 所有文件共享,用
extern引用 - 内部链接(文件 static)--- 当前文件私有,多文件同名不冲突
- 无链接(函数内 static)--- 块内私有,跨调用保持值
再加上 const 默认内部链接的特性,你就掌握了 C++ 变量链接性的全部核心规则。
互动测验(选择题)
第 1 题:全局变量的链接性
cpp
int global = 1000; // 文件顶部
static int one_file = 50;
int main() {
static int count = 0;
int temp = 5;
}
global 的存储持续性和链接性分别是?
A. 自动存储 + 外部链接
B. 静态存储 + 外部链接
C. 静态存储 + 内部链接
D. 自动存储 + 无链接
答案:B。在所有函数外定义、无 static 关键字 → 静态存储持续性 + 外部链接性。
第 2 题:static 内部链接性
两个文件都有 static int one_file,冲突吗?
A. 冲突!链接时报重复定义错误
B. 不冲突,static 让每个变量只在各自文件内可见,互不干扰
C. 取决于两个文件谁先编译
D. 冲突,但编译器会忽略其中一个
答案:B。这是 static 内部链接性的核心价值:不同文件可以有同名的 static 变量,各用各的。
第 3 题:静态局部变量 vs 自动变量
cpp
static int count = 0; // 函数内部
和普通局部变量最大的区别是什么?
A. count 只能在函数内使用,temp 可以在整个文件使用
B. count 只初始化一次,函数多次调用之间保持值;temp 每次调用都重新创建
C. 没区别
D. count 是全局的,temp 是局部的
答案:B。
第 4 题:const 的链接性
cpp
const int MONTHS = 12; // 文件顶部
MONTHS 的链接性是什么?
A. 外部链接性
B. 内部链接性(等价于 static const int MONTHS = 12;)
C. 无链接性
D. 动态链接性
答案:B。C++ 中全局 const 默认内部链接性,这是 C++ 和 C 的区别之一。
第 5 题:extern 关键字
cpp
// main.cpp
int global = 1000;
// support.cpp
extern int global; // 这行做了什么?
A. 定义了一个新的 global 变量
B. 声明引用 main.cpp 中的 global,让当前文件也能访问它
C. 给 global 赋值为 0
D. 这行代码会报错
答案:B。extern 只是声明不是定义,告诉编译器"这个变量在其他文件定义了,链接时去找"。
练习题:银行取号系统
模拟银行的取号排队系统:
cpp
int take_number(); // 每次调用返回一个递增的号码
int current_number(); // 返回当前最新的号码
要求:
- 用一个
static局部变量在take_number内部维护号码 take_number每次调用号码 +1- 写个循环模拟 5 个客户取号,验证号码是 1, 2, 3, 4, 5
cpp
int main() {
for (int i = 0; i < 5; i++)
std::cout << "您的号码是: " << take_number() << std::endl;
std::cout << "当前最新号码: " << current_number() << std::endl;
return 0;
}
习题 2:多文件全局计数器
创建两个文件:
counter.cpp:
- 定义一个外部链接的全局变量
int call_count = 0; - 定义一个函数
void increment(),将call_count加 1
main.cpp:
- 用
extern声明引用call_count - 调用
increment()3 次 - 每次调用后输出
call_count的值
习题 3:分析题
cpp
// 文件A.cpp
static int x = 10;
// 文件B.cpp
static int x = 20;
问:这两个文件能编译通过吗?x 的值会冲突吗?为什么?
习题 4:分析题
以下代码的输出是什么?
cpp
int value = 100;
int main() {
int value = 200;
{
int value = 300;
std::cout << value << std::endl;
std::cout << ::value << std::endl;
}
std::cout << value << std::endl;
return 0;
}
先写出你的答案,再实际运行验证。