#include
const int A = 10;
int a = 20;
static int b = 30;
int c;
int main(void)
{
static int a = 40;
char b[] = "Hello world";
register int c = 50;
printf("Hello world %d\n", c);
return 0;
}
函数作用域(Function Scope),标识符在整个函数中都有效。只有语句标号属于函数作用域。标号在函数中不需要先声明后使用,在前面用一个goto
语句也可以跳转到后面的某个标号,但仅限于同一个函数之中。
文件作用域(File Scope),标识符从它声明的位置开始直到这个程序文件的末尾都有效。例如上例中main
函数外面的A
、a
、b
、c
,还有main
也算,printf
其实是在stdio.h
中声明的,被包含到这个程序文件中了,所以也算文件作用域的。
块作用域(Block Scope),标识符位于一对{}括号中(函数体或语句块),从它声明的位置开始到右}括号之间有效。例如上例中main
函数里的a
、b
、c
。此外,函数定义中的形参也算块作用域的,从声明的位置开始到函数末尾之间有效。
外部链接(External Linkage),如果最终的可执行文件由多个程序文件链接而成,一个标识符在任意程序文件中即使声明多次也都代表同一个变量或函数,则这个标识符具有External Linkage。具有External Linkage的标识符编译后在符号表中是GLOBAL
的符号。例如上例中main
函数外面的a
和c
,main
和printf
也算。
内部链接(Internal Linkage),内部链接常指一个程序文件中全局变量,可以被程序文件内各个子程序访问,这在编译过程中处理,和link阶段不发生关系。如果变量前加了static,那么它永远不会被外部程序访问,它不会被编译程序写入目标代码的链接区。例如上例中main
函数外面的b
。如果有另一个foo.c
程序和main.c
链接在一起,在foo.c
中也声明一个static int b;
,则那个b
和这个b
不代表同一个变量。具有Internal Linkage的标识符编译后在符号表中是LOCAL
的符号,但main
函数里面那个a
不能算Internal Linkage的,因为即使在同一个程序文件中,在不同的函数中声明多次,也不代表同一个变量。
无链接(No Linkage)。除以上情况之外的标识符都属于No Linkage的,例如函数的局部变量,以及不表示变量和函数的其它标识符。
static
,用它修饰的变量的存储空间是静态分配的,用它修饰的文件作用域的变量或函数具有Internal Linkage。
register
,编译器对于用register
修饰的变量会尽可能分配一个专门的寄存器来存储,但如果实在分配不开寄存器,编译器就把它当auto
变量处理了,register
不能修饰文件作用域的变量。现在一般编译器的优化都做得很好了,它自己会想办法有效地利用CPU的寄存器,所以现在register
关键字也用得比较少了
extern
,上面讲过,链接属性是根据一个标识符多次声明时是不是代表同一个变量或函数来分类的,extern
关键字就用于多次声明同一个标识符,下一章再详细介绍它的用法
typedef
,在“sizeof运算符与typedef类型声明”一节讲过这个关键字,它并不是用来修饰变量的,而是定义一个类型名。在那一节也讲过,看typedef
声明怎么看呢,首先去掉typedef
把它看成变量声明,看这个变量是什么类型的,那么typedef
就定义了一个什么类型,也就是说,typedef
在语法结构中出现的位置和是面几个关键字一样,也是修饰变量定义的,所以从语法(而不是语义)的角度把它和前面几个关键字归类到一起
静态生存期(Static Storage Duration),具有外部(GLOBAL)或内部(LOCAL)链接属性,或者被static
修饰的变量,在程序开始执行时分配和初始化一次,此后便一直存在直到程序结束。这种变量通常位于.rodata
,.data
或.bss
段,例如上例中main
函数外的A
,a
,b
,c
,以及main
函数里的a
自动生存期(Automatic Storage Duration),链接属性为无链接并且没有被static
修饰的变量,这种变量在进入块作用域时在栈上或寄存器中分配,在退出块作用域时释放。例如上例中main
函数里的b
和c。
动态分配生存期(Allocated Storage Duration),以后会讲到调用malloc
函数在进程的堆空间中分配内存,调用free
函数可以释放这种存储空间
定义在所有函数外部的变量称之为全局变量。全局变量可以在任意地方使用。
定义在函数内部的变量称之为局部变量。局部变量只能在所定义的函数函数内部中使用。
静态变量的存放地址,在整个程序运行期间都不发生改变。全局变量都是静态变量,用'static'修饰的局部变量也是静态变量。未初始化的静态变量将被编译器初始化为0.
标识符:变量名、函数名、类型名统称为标识符。标识符起作用的范围称之为标识符的作用域。
单文件的程序中,结构、函数和全局变量的作用域是其定义所在的整个文件。
函数形参的作用域是整个函数。
局部变量的作用域,是所在的语句块,即{............}中的内容。
同名标识符的作用域,可能一个被另一个包含。则在小的作用域中,作用域大的那个标识符被屏蔽,不起作用。
全局变量(静态变量)的生存期,从程序开始到整个程序的结束。
静态局部变量的的生存期,从定义它语句的第一次执行开始,到整个程序的结束。
函数形参的生存期,从函数执行开始,到函数返回时结束。
非静态局部变量的生存期,从执行到定义它的语句开始,一旦执行到其作用于外,生存期便终止。