02C++ 静态变量与链接性

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;
}

先写出你的答案,再实际运行验证。

相关推荐
别动我齐刘海5 小时前
C++ 工程化学习
java·linux·前端·c++·学习·机器学习·机器人
程序员老陆5 小时前
C++ std::promise深度解析
c++·程序设计
青少儿编程课堂6 小时前
迪杰斯特拉(Dijkstra)算法:单源最短路与赛场路线规划
c++·python·算法·bfs·信息学竞赛
code2cat7 小时前
Java进阶篇之AtomicLongFieldUpdater:把累计额度留在对象字段中
java·cas·并发编程
WeiXin_DZbishe7 小时前
基于springboot城市公交实时运营与调度系统-计算机毕设 附源码【课程设计】66173
java·vue.js·spring boot·后端·python·php·课程设计
泡茶喝茶写代码7 小时前
A股量化数据工程:从 REST 接口到策略信号(第 18 篇):放量缩量异动扫描
java·python·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口
wewowilling7 小时前
认识微服务
java·运维·微服务·springcloud
代码什么用7 小时前
Java Web 7-10
java·servlet
沫璃染墨8 小时前
《Qt从零入门系列(十三):QSS样式表详解——从选择器与层叠规则到控件美化实战》
开发语言·css·c++·qt·ui·qt6.3·qss