12.1 存储类
12.1.1 作用域
定义:作用域描述了程序中可以访问一个标识符的一个或多个区域。
分类:
- 代码块作用域:在代码块中定义的变量具有代码块作用域,从定义处到包含该定义的代码块的末尾,该变量可见。
- 函数原型作用域:在函数原型中定义的变量具有函数原型作用域,从定义处到函数原型的末尾,该变量可见。
- 文件作用域:在所有函数之外定义的变量具有文件作用域,从定义处到包含该定义的文件的末尾,该变量可见。
说明:
- 代码块:包含在开始花括号和对应的结束花括号之内的一段代码。C99扩展到由循环语句或 if 语句所控制的代码。
- 文件作用域变量也被称为全局变量。
12.1.2 链接
定义:链接描述了程序的某个单元可被链接到其他哪些地方。
分类:
- 外部链接:具有文件作用域的变量可能有外部链接,该变量可以在一个多文件程序的任何地方使用。
- 内部链接:具有文件作用域的变量可能有内部链接,该变量可以在一个文件的任何地方使用。
- 空链接:具有代码块作用域或函数原型作用域的变量有空链接,该变量由其所在的代码块或函数原型所私有。
说明:
- 变量的作用域以及它的链接一起表明程序的哪些部分可以通过变量名来使用该变量。
- 区分一个文件作用域变量是具有内部链接还是外部链接:外部定义中是否使用了类型说明符 static:
int giants = 5; //文件作用域,外部链接
static int dodgers = 3; //文件作用域,内部链接
int main()
{
...
}
...
12.1.3 存储时期
定义:变量在内存中保留的时间。
分类:
- 静态存储时期:具有文件作用域的变量具有静态存储时期,该变量在程序执行期间将一直存在。
- 自动存储时期:具有代码块作用域的变量一般情况下具有自动存储时期。当程序进入该变量的代码块时,将为该变量分配内存;当程序退出代码块时,分配的内存将被释放。
C 使用作用域、链接和存储时期来定义5中不同的存储类:
存储类 | 存储时期 | 作用域 | 链接 | 声明方式 |
自动 | 自动 | 代码块 | 空 | 代码块内 |
寄存器 | 自动 | 代码块 | 空 | 代码块内,使用关键字 register |
具有外部链接的静态 | 静态 | 文件 | 外部 | 所有函数之外 |
具有内部链接的静态 | 静态 | 文件 | 内部 | 所有函数之外,使用关键字 static |
具有空链接的静态 | 静态 | 代码块 | 空 | 代码块内,使用关键字 static |
12.1.4 自动变量
int main(void)
{
auto int plox; //自动变量
- 默认情况下,在代码块或函数的头部定义的任何变量都属于自动存储类,也可显式地使用关键字 auto。
- 代码块作用域和空链接表明:只有在变量定义所在的代码块才可以通过变量名访问该变量。
- 如果在内层代码块定义了一个与外层代码块变量同名的变量,称之为内层定义覆盖了外部定义,但当程序退出内层代码块是,外部变量重新恢复使用。
- 除非显式地初始化自动变量,否则它不会被自动初始化。
12.1.5 寄存器变量
int main(void)
{
register int quick; //寄存器变量
- 使用存储类说明符 register 可以声明寄存器变量。
12.1.6 具有代码块作用域(空链接)的静态变量
int main(void)
{
static int stay; //具有代码块作用域的静态变量
- 使用存储类说明符 static 在代码块内声明可以创建具有代码块作用域(空链接)的静态变量。
- stay 只在编译时初始化一次。
- 如果不显式地对静态变量进行初始化,它们将被初始化为0。
- 对函数参量不能使用 static,例如:int work(static int flu); //不允许
12.1.7 具有外部链接的静态变量
int Errupt; //定义声明
extern char Coal; //其他文件中定义的变量
int main(void)
{
extern int Errupt; //引用声明
...
- 在所有函数外部声明可以创建具有外部链接的静态变量。
- 具有外部链接的静态变量可以被程序的任一文件中包含的函数使用。
- 如果变量是在别的文件中定义的,则必须使用 extern 来声明该变量。
- 一个外部变量只可进行一次初始化,且一定是在变量定义处进行。
- 如果不显式地对外部变量进行初始化,它们将被初始化为0。
- 只可以用常量表达式来初始化文件作用域变量。
- 变量 Errupt 声明了两次,第一次声明称为定义声明,第二次声明称为引用声明。extern 表明该声明不是一个定义,它指示编译器参考其他地方。
- 这一存储类型又被称为外部存储类,这一类型的变量被称为外部变量。
12.1.8 具有内部链接的静态变量
static int svil = 1; //具有内部链接的静态变量
int main(void)
{
- 使用存储类说明符 static 在所有函数外部声明可以创建具有内部链接的静态变量。
- 具有内部链接的静态变量只可以被与它同文件中的函数使用。
12.1.9 多文件
- 复杂的 C 程序往往使用多个独立的代码文件。ANSI C 通过在一个文件中定义变量,在其他文件中引用声明这个变量实现共享。
- 除非在第二个文件中通过引用声明(使用 extern)声明了该变量,否则在第一个文件中定义的外部变量在第二个文件中不可用。
12.2 存储类说明符
- 不可以在一个声明中使用一个以上存储类说明符
auto | 只能用在具有代码块作用域的变量声明中:自动变量 | |
register | 只能用在具有代码块作用域的变量声明中:寄存器变量 | |
static | 用与具有代码块作用域的变量的声明时,表明该变量具有静态存储类型:具有空链接的静态变量 | |
用与具有文件作用域的变量的声明时,表明该变量具有内部链接:具有内部链接的静态变量 | ||
extern | 若包含 extern 的声明具有文件作用域(引用声明),所指向的变量必然具有外部链接:具有外部链接的静态变量 | |
若包含 extern 的声明具有代码块作用域(引用声明) | 所指向的变量可能具有外部链接:具有外部链接的静态变量 | |
所指向的变量可能具有内部链接:具有内部链接的静态变量 |
12.3 存储类和函数
函数分类:
- 外部函数(默认):可被其他文件中的函数调用。例:double gama();
- 静态函数:只可以在定义它的文件中使用。例:static double delta();
- 内联函数:
说明:
- 在一个文件中定义了一个静态函数 fun1() 时,仍可以在其他文件中定义和使用与静态函数 fun1() 具有相同名称的不同函数。
- 通常使用关键字 extern 来声明在其他文件中定义的函数。
- 尽可能保持每个函数的内部工作对该函数的私有性,只共享那些需要共享的变量。
12.4 随机数函数和静态变量
ANSI C 程序库提供了随机数函数 rand(),并提供了一个可移植的标准算法来产生随机数。事实上 rand() 是一个“伪随机数发生器”,这种方案始于一个“种子” 的数字,函数使用这个种子来产生一个新数,而这个新数又称为新的种子。因此随机数函数必须记下上次被调用时所使用的种子,因此需要一个静态变量。但如果每次运行时都从同一个种子开始,函数产生的随机数就不“随机”了。因此需要通过一个重置种子的函数 srand()。
12.6 分配内存:malloc() 和 free()
- 函数 malloc() 接受一个参数:所需内存字节数。然后 malloc() 找到可用内存中一个大小合适的块。
- malloc() 分配了内存,并返回那块内存第一个字节的地址。将该地址赋值给一个指针变量,就可使用该指针来访问那块内存。
- ANSI C标准规定,将malloc() 返回值定义为指向 void 的指针类型,这一类型被用作“通用指针”。也可以对指针进行类型指派。
- 如果 malloc() 找不到所需的内存块,它将返回空指针。
double *ptd;
ptd = (double *)malloc(30 * sizeof(double)); //请求30个double类型值得空间,并使ptd指向该空间所在位置
- 可以使用表达式 ptd[0] 来访问内存块的第一个元素,以此类推。
一般地,对应每个 malloc() 调用,应调用一次 free()。
- 函数free() 接受一个参数:之前 malloc() 返回的地址。然后 free() 释放先前分配的内存。这样所分配内存的持续时间从调用 malloc() 分配内存开始,到调用 free() 释放内存供再使用为止。
- 程序还可调用函数 exit(),用来在内存分配失败时结束程序。exit(EXIT_FAILURE) 表示程序异常终止。
- 函数 malloc() 、free()、exit() 在头文件 stdlib.h 中定义。
12.6.1 free() 的重要性
程序调用 malloc(),被分配的内存所使用的内存数量只会增加,造成内存泄漏,通过在函数末尾处调用 free() 可防止该问题。
12.6.2 函数 calloc()
- 函数 calloc() 接受两个参数:第一个参数是所需内存单元的数量,第二个参数是每个单元以字节计的大小。参数都是无符号整数。
- calloc() 分配一块内存,并返回一个 void 指针。
- calloc() 将块中的全部位都置为0。
- free() 也可用来释放由 calloc() 分配的内存。
12.6.4 存储类与动态内存分配
内存可分为三个独立的部分:
- 具有外部链接的、具有内部链接的、具有空链接的静态变量的内存。——内存在程序开始执行时被分配,并在整个程序运行期间一直存在。
- 自动变量的内存。——变量所用内存在程序执行到该变量所在代码块时开始分配,在退出代码块时释放。
- 动态分配的内存。——在调用 malloc() 或相关函数时产生,在调用函数 free() 时释放。
12.7 ANSI C 的类型限定词
一个变量是以它的类型和存储类表征的。
const | 将数据限定为不变的,不能通过赋值、增量或减量运算来修改变量值 | |
volatile | 数据除了可被程序改变以外还可被其他代理改变 | |
restrict | 只可用于指针,表明指针是访问一个数据对象的惟一且初始的方式 | 告诉编译器可以自由地做一些有关优化的假定 |
告诉用户仅使用满足 restrict 要求的参数 |