1.作用域
1.1 概述
变量在程序的某个部分声明时,只能在程序的一定区域内才能访问,这个区域就成为作用域。作用域分为四种——文件作用域、函数作用域、代码块作用域和原型作用域。编译器通过变量声明的位置来确定作用域
1.2 文件作用域
当变量在所有代码块之外声明时就在文件作用域中,文件作用域的范围是从变量声明处一直到文件的结束。但如果声明在“.h”文件中,并且该文件被其他文件用#include 所包含,那该变量的作用域也会相应的扩大到包含文件的结束。
1.3 函数作用域
函数作用域只适用于语句标签,语句标签用于goto语句。一个函数作用域内的语句标签必须唯一。
1.4 代码块作用域
一对花括号之间的所有语句成为代码块作用域。在一个代码块作用域开始定义的变量可以被该代码块内的所有语句使用。
下面考虑两种情况:
代码块之间的嵌套
{
int a = 100;
{
int a;
}
}
此时内层的a就会把外层的a隐藏,如果修改内层a的值,不会影响外层的a
非嵌套的两个代码块
如果两个代码块之间没有交集,那么一个代码块的内的就语句不能使用另一个代码块内的变量。
1.5原型作用域
原型作用域适用于在函数声明中的参数名。
2.链接属性
2.1 概述
链接属性决定如何处理位于不同文件内的标识符。链接属性共有三种——external(外部)、internal(内部)、none(无)。
- 链接属性为none的标识符不论在那个文件内都是独立的个体。
- 链接属性为internal的标识符在同一个原文件内的所有声明均指向同一个实体
- 链接属性为external的标识符不论声明多少次,位于几个原文件内均表示同一实体
在文件作用域内声明的变量或是函数,在缺省的条件下链接属性为external,其余的为none
2.2 extern关键字
如果使用externa关键字声明某一个变量,说明该变量在别的地方定义的,可能位于别的文件也可能位于当前文件。该关键字制定标识符的链接属性为external
2.3 static关键字
如果一个标识符的链接属性为external,那么static可以将其链接属性改为internal
2.4 5个实验
实验1
//test1.c
int a = 100;
void printfa1()
{
printf("The value of a in test1 : %d", a);
}
//test2.c
int a = 20;
void printfa2()
{
printf("The value of a in test2 : %d", a);
}
//main.c
#include <stdio.h>
#include <stdlib.h>
void printfa1();
void printfa2();
int main()
{
printfa1();
printfa2();
return 0;
}
编译器报错:multiple definition of `a’
结论:在不同文件中不能包含同名的标识符
实验2
给test2.c中的a加上extern标识符
//test1.c 不变
//test2.c
extern int a;
void printfa2()
{
printf("The value of a in test2 : %d\n", a);
}
//main.c
#include <stdio.h>
#include <stdlib.h>
void printfa1();
void printfa2();
int main()
{
printfa1();
printfa2();
extern int a;
a = 10;
printfa1();
printfa2();
return 0;
}
编译通过,成功输出100 和 10,说明3个a都是以一个变量
结论:通过extern关键字可以访问由别的文件定义的变量
注:不能使用extern修饰变量的同时给该变量赋初值,这样编译器也会报错
error: ‘XX’ has both ‘extern’ and initializer
原因是因为该变量实在别的地方定义的所以不能给他赋初值。
实验3
给test1.c中的a用static修饰,其他文件不变,编译无法通过,说明用static修饰的变量只能用于声明的文件中,继续修改test1.c、test2.c和main.c
//test.c
static int a = 100;
void printfa1()
{
extern int a;
printf("The value of a in test1 : %d\n", a);
}
//test2.c
int a = 9;
void printfa2()
{
printf("The value of a in test2 : %d\n", a);
}
//main.c
#include <stdio.h>
#include <stdlib.h>
void printfa1();
void printfa2();
int main()
{
printfa1();
printfa2();
extern int a;
a = 10;
printfa1();
printfa2();
return 0;
}
输出为100 9 100 10
说明由static修饰的变量既不能被外部访问也不能使用外部文件的同名变量
实验4
修改test1.c中的printfa1函数,用static修饰
//test1.c
static int a = 100;
static void printfa1()
{
extern int a;
printf("The value of a in test1 : %d\n", a);
}
编译出错,main函数中的printfa1中未定义,说明该函数只能在定义的文件中使用
3.储存类型
3.1 概述
储存类型分为普通内存,运行时内存和硬件寄存器
- 在代码块之外声明的变量总是储存在静态内存中,这种变量称为静态变量。无法改变这种变量的储存类型。这种变量在程序运行之前创建,在整个程序运行时一直存在直至结束才被销毁。
- 在代码块内声明的变量为自动的,储存在堆栈内。每次函数被调用时,这些变量的储存的位置可能不同。如果用static来修饰一个函数内的变量,就可以将他转变为静态的,但是这不会改变的该变量的作用域,只是每次函数被调用是该变量的值仍未上次函数结束时的值。
- 用register修饰的变量成为寄存器变量,通常,这种变量的访问速率比在内存中的变量访问速度快。
4.static关键字
当static用于代码块之外时,static用于修改标识符的链接属性,从external修改为static
当static用与代码块内部时,static用于修改标识符的储存类型,将其从自动变量修改为静态变量