各类变量生存周期及存储空间

 

一、运行时环境简介

 

程序在执行期间,将在其自己的逻辑地址空间内运行,其中每个程序值都在这个空间内有一个地址。一种典型的程序空间模式如下图:

 

首先,运行时的数据包含数据区和代码区。图中的Text就是代码区,存储目标代码。数据区包括图中的Data、BSS、Heap和Stack。

1.(常量区)Data区主要存储常量数据,主要是需要较多空间的常量,如 char s[32]=“Hello World”;中的“Hello World”。

2.(全局区)BSS主要存储全局和静态变量。

3.(堆区)Heap是动态存储空间,主要用于程序在运行过程中动态申请存储空间。

4.(栈区)Stack为运行时的函数提供存储空间,局部变量就在此空间中。当然,除了局部变量,stack中还存放实现函数调用所必需的多种数据。

 

为了更有效地利用存储空间,将Heap和Stack采取相向放置,每个区域向对方增长的形式。在具体实现时,栈向较低地址方向增长,而堆向较高地址方向增长。

 

二、局部变量和全局变量

在函数内部定义的变量将会分配在栈上,当函数被调用时,该变量才会被分配;当函数返回后,该变量就将被释放,因此,局部变量仅仅在该函数内部范围有效,只能在该函数内使用,因此称为“局部变量”,也称为“内部变量”。

局部变量的生存周期为函数的一次调用周期。

说明:

不同函数可以使用相同名字的局部变量,相互之间不会发生指代不明的情况,main函数虽然比较特殊,但是在局部变量的使用特点上与其他函数没有区别。最后,用于接收或存储参数的形式参数也被视为局部变量,同样可以按照局部变量的方式使用。

在函数外部定义的变量将会分配在BSS区,其有效与否与函数调用过程无关,因此,其他函数均可以访问此类变量,称为“全局变量”。

1)全局变量的作用范围:全局变量可被本文件中的其他函数访问,其有效范围是从定义变量的位置开始到该文件结束。在此有效范围内的所有函数都可以访问此变量,从而可以将全局变量作为函数之间交换信息的通道。

2)全局变量的生存周期:全局变量在程序执行过程中始终占用存储单元,即全局变量的生存周期为程序的生存周期。因此,若大量使用全局变量,可能使得程序占据存储单元过多,从而降低其他程序的可用储存空间。

 

三、动态存储和静态存储

局部变量和全局变量主要是从存储空间角度对变量加以归类,从变量的生存周期来看,可以分为静态存储和动态存储。

静态存储方式是指变量的空间分配不以程序运行状况决定,而是程序运行期间固定分配的方式。

动态存储方式下,变量的分配将根据程序运行情况(如函数调用、动态内存分配等)来进行。如函数调用发生时分配变量空间,函数调用结束时释放变量空间。

下面通过例子来理解变量生存期。

例1:(全局变量和局部变量生存周期)

int global_a;
int local_1()
{
    char local_1_c;
    local_2();
}

int local_2()
{
    char local_2_c;
}
int main()
{
    int main_i;
    for(main_i=0;main_i<3;main_i++)
    {
        local_1();
    }
    return 0;
}

 

1)假定t=1时程序启动,全局变量global_a就开始存在,直到程序结束;main函数中的main_i也开始存在,直到退出main函数。

2)t=2时,main函数调用local_1函数时,local_1_c变量被分配空间,形成3个变量同时存活情况。

3)t=3时,local_1函数调用local_2函数,因为local_2_c变量也会被分配,形成4个变量在程序中共存的情况。

4)t=4时,local_2函数运行结束,local_2_c变量将被释放,导致t=5时只有local_1_c、main_i和global_a共存的状况。

5)t=5时,local_1函数运行结束,因此local_1_c变量将被释放。导致t=5时只有main_i和global_a共存的状况。

此后情况与先前类似。

 

从上例可以看到,C程序从空间作用域出发,对全局变量采取默认静态分配策略,而对局部变量采取默认动态分配策略。C程序还提供了显式指出存储类别的变量分配方法。在C程序中,可以通过关键字auto和static来指定存储类别。auto类别的变量将动态地分配存储空间,数据储存在动态存储去中,如函数的形式参数和局部变量的缺省分配。而static类别的变量将存储在静态数据区,在程序执行期间一直存活。

void auto_static();
int main()
{
    int i;
    for (i=0;i<5;i++)
    {
        auto_static();
    }
    printf("\n");
    exit(0);
}

void auto_static()
{
    int var_auto=0;
    static int var_static=0;
    printf("var_auto is %d--",var_auto++);
    printf("var_static is %d\n",var_static++);
}

程序运行结果:

 

 函数auto_static中,定义说明语句int var_auto=0;表明var_auto是局部变量,由于没有指定存储类别,所以默认采用动态存储方式。

定义说明语句

static int var_static=0;

 表明var_static是局部变量,同时指定存储类别为static,所以将采取静态存储方式,var_static将在程序开始后分配内存空间,而无须等待auto_static()函数调用的发生。

四、内部函数和外部函数

当一个程序由多个源文件组成时,可以指定一个文件内的函数能被其他文件调用,也可以指定该函数只能被本文件使用。从这个意义上说,函数可以分为外部函数和内部函数,并通过指定函数类别限定符限制函数的相互调用。

函数在本质上是外部的,函数名同变量名一样遵循作用域规则。C语言不允许函数嵌套定义,各个函数之间是平行并列的关系,相互之间可以调用,因而函数具有全局的有效范围。但每个函数定义都附属于某个程序文件,类似于变量。我们可以决定该函数的可被调用范围是所有程序文件还是仅限于该函数定义所处的文件,即指定该函数是外部函数还是内部函数。

1.外部函数

在定义函数时,如果冠以关键字extern,则表示该函数是一个外部函数,例如:

extern double func1(double x){……}

这样函数func1可被调用范围(作用域)是所有程序文件,即它可以被任何其他函数所调用而不论这个函数是否与函数func1()处于同一个程序文件中。C语言规定,如果在定义时省略extern关键字,则默认为外部函数。

2.内部函数

在定义函数时,如果冠以关键字static,则表示该函数是一个内部函数,例如:

static double func1(double x){……}

这样函数func1可被调用范围(作用域)仅限于该函数定义所处的文件,即该函数被限制为仅能被本程序文件中的函数所调用。内部函数又称静态函数,使用内部函数时,有雨函数的调用仅局限于所在的文件,因而在不同文件中可以定义相同名称的内部函数而相互不干扰,这样方便大型应用程序的编写工作。

 

 

猜你喜欢

转载自www.cnblogs.com/Tang-tangt/p/9787225.html