从链接属性和存储类型,看static和extern关键字

来源:互联网 发布:铜仁学院大数据学院 编辑:程序博客网 时间:2024/04/28 15:16

在c语言中,static关键字具有“一词多义”,他可以放在变量定义前,也可以放在函数定义前,其中位于代码块内的static和代码块外的static又是不一样的;与之相伴的extern也是同样地蜜汁神奇。以下有一个例子:

int        a = 5;extern int b;static int c; int fun1(int d){int          e = 10;register int f;        static   int g=10;        extern   int a;    ......    {        int         e;        int         a;        extern int  h;        ......           }    ......    {        int x;        int e;        ......    }  ...}static int i(void){    ....}

这里面出现了许多static和extern关键字,并且在不同地方还使用了同样的标识符,如何解释各个static和extern的作用?


首先,在C中,有一个链接属性(Linkage)的概念,描述了在不同文件中的标识符实体归属的关系。

链接属性有三种:外部(external),内部(internal)和无(none).

1.外部属性:指该属性的标识符在不同文件中都代表同一个实体,声明方法是使用关键字external,这也是最常用的方法。例如第2行就显式地声明了b是外部属性。缺省时,代码块外的变量声明/定义和函数的定义,都属于external属性,所以第1行虽然没有extern关键字,但事实上和第2行的属性并无差异。同时按照之前的定义,第4行的函数fun1,也是外部链接属性,在其他文件中可以被调用。

2.内部属性:被声明为internal的标识符在整个文件中都表示同一个实体。如果在代码块外定义的变量或者是独立的函数,想要作用域只限于本文将,就需要声明为internal类型,方法就使用关键字static,第3行就是一个声明internal属性变量的例子(然而第8行的static并不是在声明internal链接属性,后面会分析到);第27行的函数也被声明为静态函数,即该函数只能在本文件中被访问,如果一个函数只是为了服务上层函数,而不会被单独用来实现某功能,那么就应该被声明为static类型,防止在外部被误调用,也增加了程序的可读性。

3.无属性:在代码块内,没有特别声明的变量就是none属性,表示一个独立的个体,如第6行中的int e = 10; 变量e在函数fun1的中括号内、e被再次声明前起作用,即作用域是5-10,18,24行。在第11-17行的代码段中,又新定义了一个e,因此第6行中定义的e,在这个代码段中不起作用。直到第18行代码段结束,第12行的e被回收,第6行定义的e又可以使用了。同样,在第19-23行,代码段中又新定义了e,第6行定义的e不可访问,直到23行,该代码段结束。


此外,还需要引入存储类型的概念。机器中存储变量的地方有三个:普通内存、运行堆栈、寄存器,分别对应3种类型的变量:静态变量(static)、自动(automatic)、寄存器(register)。

寄存器变量就不讲了,就是使用register关键字声明的变量,让编译器把这个值放入cpu的硬件寄存器,但事实上,编译器往往不会照做,而是忽略这个命令,因为编译器有自己的方法,自动把一些使用频繁的变量放入寄存器,提升运行效率。编译器的自动处理比程序员敲出来的代码不知道高到哪里去了。所以除非你在编写一个内存只有几十K的嵌入式芯片,把内存和寄存器的使用情况搞的一清二楚,并且关闭了编译器的自动优化,编译器或许会听你的安排。

自动类型的变量也是在代码块内部声明变量的默认类型。这些变量存储在运行堆栈内,代码块一结束,变量也就丢弃了,之前所占据的内存空间就会被拿出来继续分配。

静态变量存储在静态内存中,在程序开始运行前就会被分配,知道程序运行结束,该变量的内存地址也不会发生变化。在代码块外部定义的变量默认属于静态变量(也就是我们所说的全局变量,不过从定义上来说,全局应该包括了作用域存储类型两方面的意义)。如果要在代码块内定义静态变量,就需要使用static关键字。在一些小规模的代码块中(典型的比如一个循环),为了保持变量值不变,而又想要这个变量具有局部作用域,以不至于在其他地方被误操作,就应当使用静态局部变量。例子中的第8行就是一个静态变量的声明。

对于静态变量和自动变量,还需要指出的是他们初始化的差别。由于静态变量在程序编译链接的时候可以预测到内存地址,所以即使不显式地初始化,其值也是0,如第3行的变量c;而20和21行中定义的变量是automatic类型的,运行时由操作系统分配堆栈,堆栈中的数据没有经过清零,所以初值是不确定的,并且运行两次,两次都可能不一样。


总结static的用法[1]:在用于定义函数时,或者用于代码块外部的变量声明时,static用于修改链接属性,从默认的external改为internal,但标识符的存储类型和作用域不受影响;当在代码块内部的变量声明时,static用于修改变量的存储类型,从automatic改为static,其链接属性和作用域不受影响。

--------------------------------------------------------------------------------------------------------------------------

参考文献:[1]"C与指针", Kenneth A.Reek


1 0
原创粉丝点击