前言
本章内容总结了作用域,分别对作用域、链接、存储期、存储类型进行概况。
一、数据存储🤷?♂?
众所周知,数据存储在内存中。C的强大之处能够让程序员通过内存管理系统中指定变量的作用域和生命周期。从硬件角度来看,每个值都需要占用一定的物理内存。
- 而在C中一块内存则被称为对象。该对象可被存储一个、多个、甚至没有存储值;
- 标识符(a)可用来访问对象的内容
int a = 10 ; - 存储期描述了对象在内存的保存的时间;
- 作用域和链接描述标识符,具有多种情况:
作用域:
二、存储类别🙋?♂?
2.1 作用域
用于描述程序中标识符可被访问的区域;可以说标识符声明的位置决定了它的作用域。
几种作用域:
- 块作用域;
- 函数作用域;
- 函数原型作用域;
- 文件作用域;
2.1.1 块作用域
范围:在代码块声明的起始位置到块结尾;
什么是块:位于一对花括号 之间,或者函数中任意的复合语句 ;
拓展
在C99 以前,块作用域的变量都必须声明在块的开头。而C99允许了在块中任意位置声明变量;
int i;
for(i=0; i<10; ++i)
...
for(int i=0; i<10; ++i)
...
该特性不仅仅在for循环,也应用到了while等各种语句。
举个栗子🌰:
以下标记中的变量 都为块作用域 ;
注意
应尽量避免在嵌套代码块中出现相同的变量名 。为什么编译器可以把不同代码块的变量存储域同一个内存地址呢?若不是嵌套的代码块有且仅有一个代码块处于活动状态,它们的作用域无重叠状态,且变量不能同时存在,也不会同时存在。
2.1.2 函数作用域
范围:一个标签出现,直至函数结束,且为了防止混淆,标签尽量是唯一的;
2.1.3 函数原型作用域
范围:从形参定义到原型声明结束;
- 只适用于函数原型中的形参名,使用时无需与声明中的形参名匹配;
- 类型重要,形参名可省略,但变长数组的形参名需要;
2.1.4 文件作用域(全局变量)
范围:从定义开始到文件末尾;
- 定义在代码块之外的变量,且函数名也有具有文件作用域;
2.2 链接🧝?♂?
标识符的作用域 与链接属性 相关;
几种链接属性
2.2.1 外部链接【全局作用域】
可在多个源文件内 使用,标识符不论在几个源文件内被声明多少次都属于一个实体;
- 一般使用
extern 用于外部链接,在使用其他源文件时,需声明,便于解读; - 当extern的标识符,可使用
static 来改变它的链接属性;
案例 test.h
int x = 10;
main.cpp
#include<iostream>
#include<unistd.h>
#include<cstring>
#include "test.h"
extern int x;
void test01(){
int x = 1;
std::cout << "不加extern " << x << std::endl;
}
void test02(){
std::cout << "直接使用 "<< x << std::endl;
}
void test03(){
extern int x;
std::cout << "函数内在使用extern " << x << std::endl;
}
int main(int argc, char *argv[]){
test01();
test02();
test03();
return 0;
}
运行结果:
2.2.2 内部链接【文件作用域】
该链接属性的变量只能在一个翻译单元 内使用;在同一个源文件内所有声明都指向一个实体 ,位于不同源文件则分属于不同实体。
- 一般使用
static 用于内部链接; extern 无法改变static 的链接属性;
翻译单元
即一个源文件 和它所包含的头文件 ;
2.2.3 无链接
具有块作用域、函数作用域、函数原型作用域的变量 都属于无链接变量;
2.3 存储期🧙?♂?
描述了标识符访问的对象的生存期;
几种存储期
- 静态存储期;
- 线程存储期;
- 自动存储期;
- 动态分配存储期;
2.3.1 静态存储期
存在周期:程序执行期间直至程序结束;
- 文件作用域具有静态存储器;
- 此处的静态与
static 不为同一类别; - 静态存储期包括了
内部链接 、外部链接 所有的文件作用域变量; - 块作用域也可以有静态存储期;
2.3.2 线程存储期
存在周期:从被声明到线程结束;
- 可使用
_Thread_local 即可获得变量的私有备份;
2.3.3 自动存储期
存在周期:当一个变量在块内声明,到块结束时,该变量会自动释放内存。
- 当该变量释放后,其内存地址可继续被使用;
- 块作用域(局部变量)一般都有自动存储期;
- 变长数组较为特殊,存储期是从声明到块的末尾;
2.4 变量🧚?♂?
几种变量
- 自动变量;
- 寄存器变量;
- 块作用域的静态变量;
- 外部链接的静态变量;
- 内部链接的静态变量;
2.5 存储类型🙇?♂?
2.5.1 自动变量
- 即块作用域【自动存储期、无链接】中的变量;
- 内层块会隐藏外层块的定义;
2.5.2 寄存器变量
通常变量都保存在内存当中,而寄存器变量则存储在CPU的寄存器中,更加高效。
- 寄存器的
地址无法获取 ; - 其变量与自动变量几乎
一致 ; - 具有静态存储期;
- 可声明为register的数据类型有限,例如
double 由于没有足够大的空间; - 使用只需在声明的时候加上
register 关键字;
2.5.3 块作用域【局部】的静态变量
此处的静态应理解为该变量再内存中的地址不改变,值可变;
static 变量不能使用在形参中;- 为
内部静态存储类别 ;
函数块内使用static
#include<iostream>
#include<unistd.h>
#include<cstring>
int test01(){
static int a = 10;
return a;
}
int main(int argc, char *argv[]){
int x = test01();
std::cout<< x << std::endl;
return 0;
}
由于变量在函数结束后即释放,所以无法将变量返回【有些编译器有优化,能够成功返回。如果将该变量多打印几次即会出错】。为了解决该问题,即可在变量声明前加上static 关键字,保证了函数结束后,该变量不会被释放。
2.5.4 外部链接的静态变量
外部变量,可在其他文件中进行访问;
- 外部变量具有静态存储期,即直至函数结束;
- 具有文件作用域、外部链接、静态存储期;
- 为外部存储类别;
- 在其他文件使用时,必须使用
extern 进行声明; - 若外部变量没有被初始化,则会被初始化为0;
2.5.5 内部链接的静态变量
只属于一个文件内部使用,定义在文件开头,直至文件结束;
- 具有静态存储期,文件作用域和内部链接;
- 可使用
extern 在函数内部对其声明,但不会修改它的链接属性;
小结
- 程序运行时变量存储的位置在以下三个:
- 不属于堆栈的变量 - 静态变量;
- 静态变量与外部变量未初始化则会被自动初始化为0;
- 存储类别说明符:auto、register、static、_Thread_local、typedef;
test.h
int x;
main.cpp
#include<iostream>
#include "test.h"
static int a;
extern int x;
int main(int argc, char *argv[]){
std::cout << "静态变量: " << a << std::endl;
std::cout << "外部变量: " << x << std::endl;
return 0;
}
结果
2.6 归纳表🧅
变量类型 | 声明位置 | 是否于堆栈 | 作用域 | 若声明static |
---|
全局 | 所有代码块之外 | 否 | 从声明到文件尾 | 其他源文件无法访问 | 局部 | 代码块起始处 | 是 | 整个代码块 | 不存于堆栈,直至程序结束 | 形式参数 | 函数头部 | 是 | 整个函数 | 不允许 |
|