文章目录
1. 标识符的作用范围
1.1 程序在编译时的结构
翻译单元:源代码文件.c+其所有头文件.h
1.2 作用域
不细讲
- 块作用域
- 函数作用域
- 函数原型作用域
- 文件作用域 (文件作用域变量可以是下文中的外部链接或内部链接)
1.3 链接
- 外部链接变量可在整个程序(所有源代码文件)使用
- 内部链接变量可在整个翻译单元中使用
- 无链接变量可在翻译单元中某一区域(作用域)使用(块作用域变量,函数作用域变量,函数原型作用域变量)
术语:
内部链接的文件作用域 — 简称为“文件作用域”
外部链接的文件作用域 — 简称为“全局作用域”、“程序作用域”
示例:
int glogal = 1; // 外部链接(全局作用域)
static int file = 2; // 内部链接(文件作用域)
int main{return 0;}
2. 标识符的生存期
标识符的四大存储期:
- 静态存储期
- 自动存储期
- 线程存储期
- 动态分配存储期
2.1 ▲静态存储期
具有静态存储期的变量在程序执行期间将会一直存在
所有的文件作用域变量(内部链接和外部链接)都具有静态存储期
注意:
- 对于块作用域变量,static 是表明其存储期,而非链接属性(仍未无链接变量)
- 对于文件作用域变量,内部链接变量的static 是表明其链接属性,而非存储期
//只不过static声明出来的内部链接变量是文件作用域变量,故自动具备了静态存储期
2.2 ▲自动存储期
自动存储期一般出现在块作用域中,程序进入块时,为具有自动存储期的变量分配内存,在程序推出块时,自动释放为其分配的内存
2.3 线程存储期
线程存储期用于并发程序设计中,具有线程存储期的变量,从声明到线程结束前一直存在。
2.4 动态分配存储期(malloc()与free())
使用malloc动态创建的变量,从malloc创建后到free释放前一直存在
//malloc与free
#include<stdio.h>
int main(){
double * ptr;
ptr = (double *) malloc(30 * sizeof(double));
free(ptr);
return 0;
}
malloc()参数为需要分配空间的字节数,free()参数为malloc返回的指针
malloc返回动态分配内存块的首字节地址,因此要通过一个指针来接收这个地址,分配空间失败时返回NULL空指针
为了提高程序的可读性,我们常常会使用类型转换,将malloc返回的地址转换为匹配的指针类型
free()函数只能够释放malloc()动态分配的空间,其他方式(如声明一个数组)分配的空间无法释放
3. 标识符的储存
因为标识符的储存方案由标识符的存储期和链接(作用域)决定,所以确定了存储方式也就确定了这个变量本身(我们一般根据变量的存储方式来命名变量)
五个常见储存方案
- 自动存储 - 自动变量
- 寄存器存储 - 寄存器变量
- 静态无链接存储 - 块作用域的静态变量
- 静态外部链接存储 - 外部链接的静态变量
- 静态内部链接存储 - 内部链接的静态变量
存储类别 | 存储期 | 作用域 | 链接 | 声明方式 |
---|---|---|---|---|
自动 | 自动 | 块 | 无链接 | 块内声明 |
寄存器 | 自动 | 块 | 无链接 | 块内声明,使用关键字register |
静态无链接 | 静态 | 块 | 无链接 | 块内声明,使用关键字static |
静态内部链接 | 静态 | 文件 | 内部链接 | 所有函数外,且有关键字static |
静态外部链接 | 静态 | 文件 | 外部链接 | 所有函数外 |
3.1 自动存储 - 自动变量
自动变量是声明在块中,在块作用域上,无链接,具有自动存储期的变量
为了强调变量该变量自动存储的类别,可以显示地使用关键字auto
,确保变量为自动变量(确保自动存储类别)
自动变量不会自动初始化,只能显示地初始化(而静态变量自动初始化为0)
3.2 寄存器存储 - 寄存器变量
寄存器变量是通过register声明在块中,在块作用域上,无链接,具有自动存储期的变量
与普通的自动变量相比,一般来说 寄存器不储存在内存中而是直接储存在CPU的寄存器中(“编译器不一定允许储存”),因此访问和处理寄存器变量的速度更快。且因为是储存在寄存器上,故无法获得寄存器的储存地址,不允许指针操作
3.3 静态无链接存储 - 块作用域的静态变量
块作用域的静态变量是通过使用关键字static声明在块中,在块作用域上,无链接,具有静态存储期的变量
与普通的自动变量相比,块作用域的静态变量在所在函数块调用结束后并不会释放内存,而是一直保留在内存中,知道程序运行结束
代码示例:
// 块作用域的静态变量的特点
#include<stdio.h>
void Print(){
int a = 0;
static int b = 0;
printf("局部变量a: %d, 块作用域的静态变量b: %d\n", a, b);
}
int main(){
for(int i = 0; i < 3; i++) Print();
return 0;
}
3.4 静态内部链接存储 - 内部链接的静态变量
内部链接的静态变量是通过关键字static声明在所有函数外,在文件作用域上,内部链接,具有静态存储期的变量
3.5 静态外部链接存储 - 外部链接的静态变量
外部链接的静态变量是声明在所有函数外,在文件作用域上,外部链接,具有静态存储期的变量
4. extern的使用
extern"外部",意为 所声明变量的定义在外部,在别处
4.1 作用一:防止编译器混淆同文件中的声明与定义
在使用外部链接和内部链接的静态变量时,为了防止同名变量的覆盖(防止编译器混淆声明与定义),我们按声明的位置把变量分为:
- 外部变量(external variable):,把变量的定义性声明放在了所有函数外面的变量
- 内部变量:自动变量,寄存器变量,静态变量
#include<stdio.h>
int num = 1; // 外部链接的静态变量
static char ch = 'a'; // 内部链接的静态变量
int main(){
extern int num; // 该语句为声明而非定义(再次声明了num外部链接的静态变量)
extern char ch;
/* int num; 该语句为定义,而非声明(定义了一个同名为num的局部变量) */
/* char ch; 该语句为定义,而非声明(定义了一个同名为ch的局部变量) */
return 0;
}
4.2 作用二:防止编译器混淆不同文件中声明与定义
#incldue<stdio.h>
int num1 = 1; // 此处定义了该文件中的一个外部链接的静态变量num1
extern int num2; // 此处是声明了一其他文件中的一个外部链接的静态变量num2
int main(){
return 0;
}
总结:
函数内使用extern,那么“外部”是相对于函数的,也就是指该文件的函数外
函数外使用extern,那么“外部”是相对于文件的,也就是指该文件外