现在的位置: 首页 > 综合 > 正文

C语言中标识符的作用域、命名空间、链接属性、生命周期、存储类型(下)

2013年07月26日 ⁄ 综合 ⁄ 共 4106字 ⁄ 字号 评论关闭

C语言中标识符的作用域、命名空间、链接属性、生命周期、存储类型(上)》:
 http://blog.csdn.net/daheiantian/archive/2011/01/16/6219590.aspx

 

3. 标识符的链接属性

        主要用于处理多次声明相同的标识符名称后,如何判断这些标识符是否是同一个。 
        原文对链接属性(linkage)的定义如下:An identifier declared in different scopes or in the same scope more than once can be made to refer to the same object or function by a process called  linkage.

        注意:链接属性(linkage)是相对于相同的标识符名称来说的,对于不同的标识符,没有链接属性。

        按照C99(章节6.2.2),链接属性分为三种:external(外部的), internal(内部的), none(无)。

类型

说明

默认(即不使用extern和static)

外部  external 同一个标识符,即使在不同的文件中,也表示同一个实体。 ①具有文件作用域变量函数
代码块作用域内部的函数声明
内部  internal 同一个标识符,仅仅在同一个文件中才表示同一个实体。 (如果不使用static,那么默认没有内部链接属性的标识符。只有被static修饰的具有文件作用域的标识符,才具有internal链接属性)
无  none 表示不同的实体 所有其他的标识符。如:函数的参数、代码块作用域的变量、标签等

        extern和static的使用:

        3.1  文件作用域的变量和函数定义,即在所有 代码块和参数列表之外的标识符,使用static修饰,则具有 内部链接属性。

        3.2  一个标识符声明为extern,并且前面已经对同一个标识符进行了声明,那么
              ①如果前一个声明时internal或者external,那么后一个声明与前一个相同。(即尽管后一个使用了extern,但其链接属性由前一个决定)。
              ②如果前一个声明为none,或者前一个声明在当前作用域不可见,那么这个标识符的链接属性为external。 
             举例说明并分析:(注意所有文件都在同一个工程中)

/* 文件《test1.c》 */
int a=1 ;   /* 这里的a为external */ 
int b=1;    /* 这里的b为external */ 

void print_in_test1(){
    static int a;   /* 这里是重新声明一个变量a, 并且会隐藏掉外层的a。由于是static静态类型,其默认初始化为0,所以下面的打印结果应为 0*/ 
    extern int b;   /* 虽然这里将b用extern声明,但是由于文件前面声明的b是external,所以b的链接属性也没有改变,依然是external,所以下面的打印结果应为 1 */ 
    
    printf("test1.c:  a == %d  /n", a);
    printf("test1.c:  b == %d  /n", b);
}


/*文件《test2.c》 */
static int a=2; /* 这里的a为internal */ 

void print_in_test2(){
    extern int a;  /* 虽然这里将a用extern声明,但是由于文件前面声明的a是internal,所以a的链接属性并没有改变,依然是internal */ 
    int b =2;  /* 这里b为none,不会链接到test1.c中的 b,所以下面的打印结果应为 2 */ 
    printf("test2.c:  a == %d  /n", a);     /* 所以下面的打印结果应为 2 */ 
    printf("test2.c print_in_test2() :  b == %d  /n", b);
}

void print2_in_test2(){
    extern int b;   /* b会链接到test1.c中的 b,而不是上面的函数中的 b,所以下面的打印结果应为 1 */
    printf("test2.c:  b == %d  /n", b); 
} 

/* 文件《main.c》 */
#include 
#include 

extern int a; /* 会链接到test1.c中的 a,所以下面的打印结果应该为 1 */ 

void print_in_test1();  /* 函数原型,会链接到test1.c中的 print_in_test1()*/

int main(int argc, char *argv[])
{

    void print_in_test2();  /* 函数原型,会链接到test2.c中的 print_in_test2()*/
    void print2_in_test2(); /* 函数原型,会链接到test2.c中的 print2_in_test2()*/
    
    printf("main.c:  a == %d  /n", a);
    print_in_test1();
    print_in_test2();
    print2_in_test2();
    
    system("PAUSE");	
    return 0;
}

        运行结果:

main.c:  a == 1
test1.c:  a == 0
test1.c:  b == 1
test2.c:  a == 2
test2.c print_in_test2() :  b == 2
test2.c:  b == 1

        3.3  如果不使用static和extern:
                1.对于函数声明:一定是external,无论是否在代码块内部。
                2.对于变量声明:如果在   代码块外,则是 external;否则是none

             例子可以参照上面的程序代码,《main.c》中声明函数原型时,print_in_test1()在main函数外,print_in_test2()和print2_in_test2()在main函数内,虽然位置不同,但都是external的,都会正确链接到相应的函数。

4. 变量的生命周期、存储类型

        变量的生存期(Storage durations),也就是变量的生命周期(lifetime),可以理解为:程序运行期间,变量从分配到地址地址被释放 这一过程。

        更具C99描述,变量的生存期分为三种类型:static(静态), automatic(自动), and allocated(动态分配)。

        1.  属于文件作用域(即external或internal链接属性)、以及被static修饰的变量,具有static静态生存期

        2.  链接属性为none,并且没有static修饰 的变量,具有automatic自动生存期

        3.  allocated动态分配生存期,是指使用malloc函数,在进程的堆空间分配内存的变量。

        说明:

        4.1  生命周期、存数类型 都是针对变量,对于函数等其他标识符没有这个说法。
               因为在程序运行期间,只有变量才需要分配内存和释放内存,其他的诸如函数等都不需要。

        4.2  变量的生命周期存储类型密切相关。

              ① 静态生存期的变量存储在静态内存中。其中使用static修饰的变量,在C语言书籍中也被称为“静态变量”。静态存储的变量,在程序运行之前就已经创建,在程序整个执行期间一直存在,如果声明时没有被显式的初始化,就会被自动初始化为0。          注意静态变量当然是属于静态存储方式,但是属于静态存储方式的变量不一定就是静态变量, 例如外部变量虽属于静态存储方式,但不一定是静态变量,必须由 static加以定义后才能成为静态变量。
              ② 自动生存期的变量存储于栈或寄存器中。其中在代码块内部声明的变量,在C语言书籍中也被称为“自动变量”,使用auto修饰符,默认可以省略。对于自动存储的变量当程序执行到含有自动变量的代码段时,自动变量才被创建,并且不会被自动初始化,代码段执行结束,自动变量就自动销毁,释放掉内存。如果代码段被反复执行,那么自动变量就会反复被创建和销毁。注意这一点和静态变量不同,静态变量只创建一次,到程序结束才销毁。

              ③  动态分配生存期的变量存储于中,也不会被自动初始化,使用free函数释放内存。

        4.3  修改变量的存储类型(如用static将自动变量变为静态变量),并不会修改变量的作用域,变量的作用域仍然有其声明的位置决定。

        4.4  变量的存储类型修饰符一共有五个:static、auto、register、extern、typedef。

        4.5  函数的形式参数,如果使用修饰符,只能使用register修饰,表示运行时参数存储在寄存器上。注意:形式参数是不能用auto修饰的。

5. 总结

        下图为一个变量声明,在 不同的作用域 对应的其他属性:

作用域

声明位置

链接属性

存储类型

默认初始化值

使用static修饰

file 在所有“代码块”和“参数列表”之外 external static   0 internal
block 在“代码块”或者“函数的参数列表”内部 none automatic 形式参数 调用时被初始化;代码块内部的不自动初始化 none
function 函数体内 --------- -------- 标签,不需要初始化 ---------
function prototype 声明的函数原型的参数列表中(注意与“函数定义”不同) --------- -------- 不需要初始化 ---------

 

 

本文链接:http://blog.csdn.net/daheiantian/archive/2011/01/20/6222216.aspx

 

【上篇】
【下篇】

抱歉!评论已关闭.