本文主要总结了 C 语言中变量作用域以及存储类型。
作用域
作用域有 4 种:
- 文件作用域:任何在代码块之外声明的标识符具有文件作用域(file scope),表示这些标识符从它们声明之处知道它们所在的源文件结尾都是可以访问的。
- 函数作用域:function scope,只适用于语句标签,语句标签用于
goto语句。函数作用域可以简单表述为:一个函数中所有的语句标签必须唯一(很少用)。 - 代码块作用域:位于一对花括号之间的所有语句称为一个代码块。任何在代码块开始位置声明的标识符都具有代码块作用域(block scope)。内层代码块的标识符会隐藏外层代码块的标识符。
- 原型作用域:原型作用域(prototype scope)只适用于在函数原型中声明的参数名。在原型中,参数名不是必须的。但是如果出现参数名,那么原型作用域的作用就是防止参数名与程序中其他部分的名字冲突。事实上,唯一可能出现的冲突就是在同一个原型中使用相同参数名。
链接属性
链接时编译的一部分。当相同的标识符出现在多个不同的源文件中,此时,标识符的链接属性 (linkage)决定如何处理在不同文件中的标识符。
链接属性一共有 3 种:
- external:
external链接属性的标识符无论声明多少次、是否位于同一源文件,都表示同一实体。 - internal:
internal链接属性的标识符在同一个源文件中的所有声名都指向同一个实体,但是位于不同源文件中的多个声明则分别指向不同的实体。 - none:
none属性标识符总是被当作单独的个体,即该标识符的多次声名被当作不同的实体。
属于文件作用域的声明默认是 external 链接属性。
所有函数的声明和定义默认是 external 链接属性。
关键字 extern 和 static 用于改变标识符的链接属性。
在具有 external 链接属性的标识符前加 static 关键字可以将其变为 internal 链接属性。(从技术上来说,这两个关键字只有在声明时是必须的,但有时添加这些关键字可以说让别人更容易理解你的意图。)
注意 :
static只对缺省链接属性为 external 的声明才有改变链接属性的效果。
extern 关键字规则比较复杂:
- 一般情况下,它为标识符指定 external 链接属性
存储类型
变量的存储类型(storage class)是指存储变量的内存类型,它决定了变量何时创建、何时销毁以及变量生存期多久。有 3 个地方可以存储变量:普通内存、运行时堆栈以及硬件寄存器 。
变量的缺省存储类型取决于其声名位置:
凡是在任何代码块之外声明的变量是 静态变量,存储在静态内存中,不属于堆栈内存。你无法为这列变量指定其他存储类型。静态变量在程序执行前创建,并在程序执行期间一直存在,且始终保持原来的值,除非赋予了新值(或程序结束),直到代码执行完毕后才销毁。
在代码块内部声明的变量是自动的(automatic),存储在 堆栈 中,称为 自动变量。在程序执行到声明自动变量的代码块时,自动变量才被创建;当程序执行离开该代码块时,这些自动变量就自行销毁。
对于在代码块内部声明的变量,如果加上
static关键字,可使其存储类型变为静态变量。注意:
修改变量的存储类型并不意味着修改其作用域,该变量依然只能在声明它的代码块访问。
- 函数形参不能声明为静态变量,因为实参总是在堆栈中传递给函数,用于支持递归。
- 具有 external 链接属性的实体总是具有静态存储类型。
register关键字可以将变量声明为寄存器变量。但是一般都用不到就是了!
static 关键字
- 当用于函数定义或者用于代码块之外的变量声明时,
static关键字用于修改标识符的链接属性,从 external 变为 internal,但标识符的存储类习惯和作用域不受影响。 - 当用于代码块内部的变量声明时,
static关键字会修改变量的存储类型,从自动变量变为静态变量,但是变量的链接属性和作用域不受影响。这种方式声明的变量在程序执行前创建,并在程序执行期间一直存在,直到代码执行完毕后才销毁。
总结
| 变量类型 | 声明位置 | 是否位于堆栈 | 作用域 | 若声明为 static |
|---|---|---|---|---|
| 全局 | 所有代码块之外 | 否 | 声明处到文件结尾 | 变为 internal 链接属性 |
| 局部 | 代码块起始处 | 是 | 整个代码块 | 变量变为静态变量 |
| 形参 | 函数头部 | 是 | 整个函数 | 不允许 |