您的位置:首页 > 编程语言 > C语言/C++

关键字static(c语言)

2014-03-27 19:14 211 查看
以下内容从《C与指针》(《POINTERS ON C》)第三章中摘取出来,该章节将作用域(scope)、链接属性(linkage)、存储类型(storage class)分析得很透彻。笔者会以static关键字为线索,同时将extern、register等关键字讲解清楚。先说结论:当static用于函数定义时,或用于代码块之外的变量声明时,static关键字用于改变标示符的链接属性(linkage),由external改为internal(也就是链接属性(linkage)由外部改为内部,变量或函数变为该源文件私有),但标示符的存储类型(storage
class)
作用域(scope)不受影响。用这种方式声明的函数或变量只能在声明它们的源文件中访问。当static用于代码块内部的变量声明时,static关键字用于修改变量的存储类型,从自动变量修改为静态变量,但变量的链接属性作用域不受影响。用这种方式声明的变量在程序执行之前创建,并在程序的整个执行期间一直存在,而不是每次在代码块开始执行时创建,在代码块执行完毕后销毁。要理解结论,首先要了解作用域(scope)链接属性(linkage)存储类型(storage
class)

作用域:

当变量在程序的某个部分被声明时,它只有在程序的一定区域才能被访问。这个区域由标示符的作用域作用域(scope)决定。标示符的作用域就是程序中该标示符可以被使用的区域。位于一对花括号之间的所有语句成为一个代码块。任何在代码块的开始位置声明的标示符都具有代码块作用域(block scope)。当代码块处于嵌套状态时,声明于内层代码块的标示符的作用域到达该代码块的尾部便告终止。然而,如果内层代码块有一个标示符的名字与外层代码块的一个标示符同名,内层的那个标示符就将隐藏外层的标示符——外层的那个标示符无法在内层代码块中通过名字访问。

链接属性:

当组成一个程序的各个源文件分别被编译后,所有的目标文件以及那些从一个或多个函数库中引用的函数链接在一起,形成可执行程序。标示符的链接属性(linkage)决定如何处理在不同文件中出现的标示符。链接属性(linkage)一共有三种:

external(外部)
internal(内部)
none(无)

没有链接属性(linkage)的标示符(none)总是被当作单独的个体,也就是说该标示符的多个声明被当作独立不同的实体。属于internal链接属性(linkage)的标示符在同一个源文件内的所有声明中都指向同一个实体,但位于不同源文件的多个声明则分属不同的实体。最后,属于external链接属性(linkage)的标示符不论声明多少次、位于几个源文件都表示同一个实体。关键字extern和static用于在声明中修改标示符的链接属性(linkage)。如果某个声明在正常情况下具有external链接属性(linkage),在它前面加上static关键字可以使它的链接属性(linkage)变成internal。例如对于一个源文件中代码块外声明的变量b:
static int b;

或者对于一个函数:
static int c(int d)

则它们不能被其它源文件调用。static关键字只对缺省链接属性(linkage)为external的声明才有改变链接属性(linkage)的效果。这一点非常重要。external关键字为一个标示符制定external链接属性(linkage)后,这样就可以访问在其他任何位置定义的这个实体。例如:
static int i;
int func(){
    int j;
    extern int k;
    extern int i;
}

k被指定为external链接属性(linkage),函数可以访问在其它源文件声明的外部变量了。需要注意的一点是:当extern关键字用于源文件中的一个标示符的第一次声明时,它指定该标示符具有external链接属性(linkage),但是,如果它用于该标示符的第二次或以后的声明时,它并不会更改第一次声明的所指定的链接属性(linkage)。例如上面的代码,extern int i并不会修改变量i的链接属性(linkage)

存储类型:

变量的存储类型(storage class)是指存储变量值的内存类型。变量的存储类型决定变量何时创建、何时销毁以及它的值将保持多久。三个地方可以用于存储变量:

普通内存
运行时堆栈
硬件寄存器

变量的存储类型取决于它的声明位置:在任何代码块之外声明的变量总是存储于静态内存中,也就是不属于堆栈的内存,这类变量称为静态(static)变量。在代码块内部声明的变量的缺省存储类型是自动的(automatic),也就是存储在堆栈中,这类变量称为自动(auto)变量。在程序执行到声明自动变量的代码块时,自动变量才被创建,当程序的执行流离开该代码块的时候,这些自动变量便自行销毁。对这样的变量,如果给它加上关键字static,可以使它的存储类型由自动变成静态,如果变量成为了静态变量,那么整个程序执行过程中会一直存在。但这并不表示改变该变量的定义域,它只能在该代码块内部按名字访问。最后,关键字register可以用于自动变量的声明,提示它们应该存储于机器的硬件寄存器而不是内存中,这类变量称为寄存器变量。一般来说,我们把使用频率最高的变量声明为寄存器变量。最后,摘抄一个实例:
int a = 5;
    extern int b;
    static int c;
    int d(int e){
        int f = 15;
        register int b;
        static int g = 20;
        extern int a;
        ……
        {
            int e;
            int a;
            extern int h;
            ……
        }
        ……
        {
            int x;
            int e;
            ……
        }
    ……
    }
    static int i(){
    ……
    }
    ……

属于文件作用域的声明在缺省情况下为external链接属性,所以第一行的a的链接属性为external。如果b的定义在其他地方,第2行的extern关键字在技术上非必需,但在风格上还是加上这个关键字为好。第三行的关键字修改了c的缺省链接属性,把它改为了internal。声明了变量a和b(具有external链接属性)的其他源文件在使用这两个变量时实际所访问的是声明于此处的这两个变量。但是变量c只能由这个源文件访问,因为它具有internal链接属性。变量a、b、c的存储类型为静态,表示它们并不是存储在堆栈中。因此这些变量在程序执行之前创建,并一直保持它们的值,直到程序结束。第7行和第13行声明的局部变量a和b在程序中将隐藏同名的静态变量。a、b、c这三个变量的作用域为:

第1行至11行,第16行至27行
第2行至第5行,第24行至27行
第3行至27行

第4行声明了2个标示符。d的作用域从第4行直到文件结束。函数d的定义对于这个源文件中任何以后想要调用它的函数而言起到了函数原型的作用。作为函数名,d在缺省情况下具有external链接属性,所以在其他源文件只要在文件上存在d的原型,就可以调用d。如果把函数声明为static,就可以把它的链接属性由external改为internal,但这样做将使其他源文件不能访问这个函数。

参数e不具有链接属性,所以我们只能从函数内部通过名字访问它。它具有自动存储类型,所以它在函数被调用时被创建,当函数返回时消失。由于与局部变量冲突,它的作用域限于第5行到10行,第16行到18行,第22行到23行。

第5行到第7行声明局部变量,所以它们的作用域到函数结束为止。它们不具有链接属性,所以它们不能在函数的外部通过名字访问。f的存储类型是自动,当函数每次被调用时,它通过隐式赋值被初始化为15。b的存储类型是寄存器类型,它的初始值是垃圾。g的存储类型是静态,所以它在函数的整个执行过程中一直存在。当函数执行时,它被初始化为20,当函数再一次被调用时,它并不会被重新初始化。第8行的声明其实并不需要。这个代码块位于第1行声明的作用域之内。

第11、12行为代码块声明局部变量。它们都具有自动存储类型,不具有链接属性,它们的作用域延伸至15行。这些变量和先前声明的a和e不同,而且由于名字冲突,在这个代码块中,之前声明的同名变量是不能被访问的。

第13行使全局变量h在这个代码块内可以被访问。它具有external链接属性,存储于静态内存中。这是唯一一个必须使用extern关键字的声明,如果没有它,h将变成另一个局部变量。

第18、19行用于创建局部变量(自动、无链接属性、作用域限于本代码块中),这个e和参数e是不同的变量,它和第11行的声明的e也不相同。在这个代码块中,从第10行到第17行并无嵌套,所以编译器可以使用相同的内存来存储两个代码块中不同的变量e,如果想让这两个代码块中的e表示同一个变量,那么就不应该把它声明为局部变量。

最后第24行声明了函数i,它具有静态链接属性。静态链接属性可以防止它被这个源文件之外的任何函数调用。事实上,其他的源文件也可能声明它自己的函数i,它和这个源文件的i是不同的函数。i的作用域从它声明的位置直到这个源文件结束。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: