Skip to content

6.11 - 作用域和链接小结

作用域、持续时间和链接这些概念很绕人,所以我们利用这节课对它们进行一次整理和总结。有些内容我们还没有介绍,但是我们仍然将其罗列在这里,在后续学习时可以进行参考。

作用域小结

变量标识符的作用域决定了标识符在哪里可以被访问。

  • 具有块(局部)作用域的标识符,只能够在声明它们的块(及其嵌套块)中访问,这些变量包括:
  • 具有文件(全局)作用域 的变量和函数从声明位置开始,直到文件结尾都可以被访问, 包括:
    • 全局变量
    • 函数
    • 定义在全局作用域或命名空间中的用户自定义类型(包括枚举和类)

持续时间小结

变量的持续时间决定了它合适被创建和销毁。

链接小结

标识符的链接属性,决定了标识符的多个声明是否被看做同一个表示符。

  • 不具备链接属性的标识符,意味着该标识符只表示它自己,包括:
    • 局部变量
    • 定义在块中的用户自定义类型(包括枚举和类)
  • 具备内部链接属性**的标识符可以在文件内部的任何位置被访问,包括:
    • 静态全局变量(初始化或未初始化的)
    • 静态(static)函数
    • const 全局变量
    • 定义在匿名命名空间中的函数
    • 定义在匿名命名空间中的用户自定义类型(包括枚举和类)
  • 具备外部链接属性的标识符可以在定义它的文件中访问,也可以在其他文件中(通过前向声明)访问,包括:
    • 函数
    • 非常量全局变量(初始化或未初始化的)
    • 外部常量全局变量
    • 内联常量全局变量
    • 定义在命名空间或全局作用域中的用户自定义类型(包括枚举和类)

具有外部链接的标识号如果在多个cpp文件中被编译,则会导致链接器报错(因为违反了单一定义规则(one-definition-rule))。不过,也有例外的情况(对于类型、目标、内联函数和内联变量)——我们会在后续的课程中介绍相关内容。

同时,注意函数默认具有外部链接,必须要使用 static 才能使其具有内部链接。

变量作用域、持续时间和链接小结

因为变量具有作用域、持续时间和链接属性,这里我们使用表格对其进行总结:

类型 例子 作用域 持续时间 链接属性 备注
局部变量 int x; 自动
静态局部变量 static int s_x; 静态
动态变量 int *x { new int{} }; 动态
函数形参 void foo(int x) 自动
外部非常量全局变量 int g_x; 文件 静态 外部 初始化或未初始化
内部非常量全局变量 static int g_x; 文件 静态 内部 初始化或未初始化
内部常量全局变量 constexpr int g_x { 1 }; 文件 静态 内部 必须初始化
外部常量全局变量 extern const int g_x { 1 }; 文件 静态 外部 必须初始化
内联常量全局变量(C++17) inline constexpr int g_x { 1 }; 文件 静态 外部 必须初始化

前向声明小结

使用前向声明可以从其他文件中访问一个函数或者变量。声明变量的作用域与往常一样(全局变量具有文件作用域,局部变量具有块作用域)。

类型 例子 备注
函数前向声明 void foo(int x); 只需要函数原型,不能有函数体
非常量的变量前向声明 extern int g_x; 必须未初始化
常量的前向声明 extern const int g_x; 必须未初始化
Constexpr 变量前向声明 extern constexpr int g_x; 不允许,constexpr不能前向声明

存储类型说明符到底是什么?

当 static 和 extern 关键字(存储类型说明符)作为变量声明的一部分时,它们可以设定标识符的存储持续时间和链接属性。

C++ 支持 4 种存储类型说明符:

说明符 含义 备注
extern static (或 thread_local) 存储持续时间和外部链接
static static (或 thread_local) 存储持续时间和内部链接
thread_local thread 存储持续时间
mutable 即使类是const类型的,变量也允许修改
auto 自动存储持续时间 在 C++11 中已经弃用
register 自动存储持续时间并且提示编译器将其存放在寄存器中 在 C++17 中已经弃用

术语 存储类型说明符 通常只会在正式的文档中使用。