标识符:
首先,在讨论这三种东西之前,详细说明一下C语言中的标识符。
标识符是用户编程为变量、常量、函数、语句等指定的名字,就好比人名一样的东西。
标识符的命名规则如下:
1.只能由字母、数字、下划线组成,并且首字符不能是数字;
2.不能把C语言的关键字作为标识符;
3.对大小写敏感;
其次,需要明确,作用域和链接属性是在标识符范畴内讨论的,存储类型是在标识符中的变量范畴内讨论的。
作用域:
标识符的作用域就是程序中该标识符可以被使用的区域,由它的声明位置决定。
分为以下四类:
1.文件作用域 2.代码块作用域 3.原型作用域 4.函数作用域
1.文件作用域:
任何在所有代码块之外声明的标识符(变量、常量、函数名、语句等)具有,这表示这个标识符从它声明的位置到这个源文件结尾处都可以访问。
注意:在任何代码块之外声明的函数名也具有文件作用域,因为这个函数名本身并不属于任何代码块。
2.代码块作用域:
位于{}之间的所有语句称为代码块,在代码块内声明的标识符,其作用域为从其声明位置开始到这段代码块结束,它可以被这个区间内的语句访问。
注意:处于嵌套状态的代码块,当内层和层声明了相同的标识符时,外层这个标识符在内层不再起作用。编程中应该尽量避免出现这种情况。
3.原型作用域:
只适用于在函数原型中声明的参数名,作用域为函数原型中的()内,为防止在同一个原型中不止一次使用这个名字。
4.函数作用域:
只用于语句标签。
下面用一个例子说明几种作用域:
#include <stdio.h>
int a; //a具有文件作用域 void test(int *b); //b具有原型作用域 int main(void) { int c=10; //c具有代码块作用域 a=20; printf("a=%d",a); printf("Before change: c=%d",c); test(&c); printf("After change: c=%d",c); for(int d=0;d<5;d++) //d具有代码块作用域 { int e=0; //e仅在循环内起作用 printf("d=%d",d); } return 0; } void test(int *f) //f具有代码块作用域,在函数声明中起作用 { *f=30; }
链接属性:
作用域考虑的是单一源文件的情况,而链接属性是在多源文件范畴内的。链接属性决定如何处理在不同源文件中出现的相同标识符。
分为三种:
1.external(外部) 2.internal(内部) 3.none(无)
1.external:无论标识符声明多少次,位于几个源文件内,都表示同一实体;
2.internal:在同一源文件内的所有声明属于同一实体,不同源文件中的多个声明则表示不同实体;
3.none:总是被当做单独的个体,即不同的实体。
注意:
①缺省情况下,具有文件作用域的变量、函数名的链接属性为external,其他为none;
用于修改标识符链接属性的关键字:
1.static 2.extern
1.static:用于将external改为internal,如果被修改的标识符不是external,则起不到修改链接属性的作用;
2.extern:
①对于具有文件作用域的标识符,extern加不加都可以;
②当extern再是第一次用于声明标识符,那么它的作用是表示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义;
③具有extern链接属性的实体总是具有静态存储类型。
存储类型:
指存储变量值的内存类型,它决定变量的生存期。一般变量的存储类型取决于声明的位置。
分为三种:
1.静态(static)变量:在任何代码块之外声明的变量,存储于静态内存中,在程序运行之前创建,并在程序执行期间始终存在;
2.自动(automatic)变量:在代码块内部声明的变量默认为自动变量,存储于堆栈中,当程序执行代码块时才被创建,代码块执行结束时自动销毁;
默认为自动变量的原因:
①当需要时再分配内存,减少内存的总需求量;
②有效实现递归。
3.寄存器(register)变量:用于声明存储于寄存器中的自动变量;将使用频率较高的变量放入硬件寄存器,可以提高效率。
关于static的两点说明:
1.当static用来修饰函数定义、代码块之外的变量声明时,用于修改其链接属性,从external->internal,不改变作用域和存储类型(有表示“本地”之意);
2.当作用于代码块内部的变量声明时,用于修改变量的存储类型,从auto->static,不改变作用域和链接属性。