变量的存储方式与生存期

动态存储方式与静态存储方式

我们之前从变量的作用域(空间)的角度将变量分为全局变量局部变量两种。

现在,我们从**变量值存在的时间(生存期)**来进行观察。
在程序中,有的变量在程序运行的整个过程都是存在的,
而有的变量则是在调用其所在函数时才临时分配存储单元,在函数调用结束后该存储单元就马上释放了,变量也就不存在了。

依此,变量的存储有两种方式,即静态存储方式动态存储方式

静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式。
动态存储方式则是在程序运行期间根据需要进行动态的分配存储空间的方式。

就基本是字面意思。

在内存中供用户使用的存储空间可以分为下面的三部分:
(1)程序区
(2)静态存储区
(3)动态存储区

数据就是存放在动态存储去和静态存储区中的。
全局变量全部存放在静态存储区中,它们从程序开始到程序结束都存在,在程序开始执行时给全局变量分配存储区,程序执行完毕就会释放出来。

动态存储区中可以存放这些数据:
(1)函数形式参数
(2)函数定义中的没有用关键字static声明的变量(自动变量)
(3)函数调用时的现场保护和返回地址等。

这些数据都是在函数调用开始时分配动态存储空间,函数结束时释放,这里注意,是函数
如果在一个程序中两次调用同一函数,而在此函数中定义了局部变量,在两次函数调用时分配给这些局部变量的存储空间的地址可能是不相同的。

如果在一个程序中包含若干个函数,每个函数中的局部变量的生存期是程序执行周期的一部分
在程序执行过程中,先后调用各个函数,此时会动态地分配和释放存储空间。

在定义和声明变量和函数时,一般应同时指定其数据类型和存储类别,也可以采用默认方式指定。

C语言有四种存储类别:
自动的(auto)
静态的(static)
寄存器的(register)
外部的(extern)

局部变量的存储类别

1.自动变量(auto变量)

在函数中的局部变量,我们如果不去专门声明它是static(静态)存储类别,都会是动态地分配存储空间的,他们的数据就存储在动态存储区中。

在调用该函数时,系统会给这些变量分配存储空间,在函数调用结束时就会自动释放这些存储空间,这就是这类变量属于自动变量的原因。

自动变量用关键字auto作存储类别的声明。
如:

int f(int a)
   {
    auto int b,c=3;
    ...
   }
   

在这里面,b和c就是自动存储类别的变量,在书写时,auto可以省略,自动将变量指定为自动存储类别,属于动态存储方式。

2.静态存储变量(static局部变量)

在自动存储类别的基础上,如果我们想要让一个局部变量的值在函数调用后保留原来的值,即占用的存储单元不释放,
这样,我们在下次调用这个函数的时候,这个变量的值还是上一次调用结束时的值。
为了达到这样的效果,我们就需要指定这个局部变量是静态局部变量,用关键字static进行声明。

我们来看下面的程序:

#include <stdio.h>
int main()
   {
    int f(int);
    int a=2,i;
    for(i=0;i<3;i++)
    printf("%d\n",f(a));
    return 0;
   }

int f(int a)
   {
    auto int b=0;
    static int c=3;
    b=b+1;
    c=c+1;
    return(a+b+c);
   }

我们通过for循环可以看出,这个f函数是连续调用了3次。
我们直接来看三次调用的结果:
在这里插入图片描述
我们可以看出,每一次循环结束,b的值都会消失,再调用时,重新赋值为0,而c的值不会随着调用结束而消失,这就是static的作用。

下面是一些补充说明:

1.静态局部变量属于静态存储类别,在静态存储区中分配存储单元,在程序整个运行期间都不会释放。

2.对静态局部变量是在编译时赋初值的,只会赋初值一次,在程序运行时,他已经是有初值了。因为程序运行时,调用函数后他的值并不会消失,也就不需要再一次赋初值。以后每次调用函数时只是保留上次函数调用结束时的值。

相对而言,对自动变量赋初值不是在编译时进行的,而是在函数调用时进行赋值,每重新调用一次函数就赋一次值。

3.如果在定义局部变量时不赋初值的话,对于静态局部变量来说,编译时会自动赋初值为0(数值型变量)或空字符’\0’(字符型变量)。

而对于自动变量来说,他的值就不确定了。因为他调用时会重新分配存储单元,那么这个存储单元里面有什么就不是确定的。

那么利用静态局部变量的特性,可以进行一些运算:

#include <stdio.h>
int main()
   {
    int fac(int n);
    int i;
    for(i=1;i<=5;i++)
       printf(""%d!=%d\n",i,fac(i));
    return 0;
   }

int fac(int n)
   {
    static int f=1;
    f=f*n;
    return (f);
   }

这个程序就可以用来求阶乘,它的这个被调用函数里面就是根据n的值来确定被调用的次数,而f是一个静态局部变量,这样,就可以从1累乘到n,也就实现了阶乘的运算。

对于静态存储而言,它的使用比较占内存,不像动态存储那样使用后可以给其他变量使用。
另外,调用次数过多时,也会分不清静态变量前值是什么,还是有很大的弊端的。

3.寄存器变量(register变量)

如果有一些变量使用频繁,存取变量的过程就会浪费大量的时间。
在这种情况下,为节省时间,就允许将局部变量的值存放在CPU中的寄存器中,需要使用变量时,可以直接从寄存器中取出参加运算,不需要再放到内存当中。
存到寄存器中当然需要位置,但是它的存取速度远高于内存,这样就会再大量的存取的条件下节省时间。

这种变量就叫做寄存器变量,用关键字register进行声明。

这个只是需要了解的。我们只需要知道它是存放在CPU 中的寄存器中。

全局变量的存储类别

对于全局变量,它们都是存储在静态存储区的。
也正因此,它们的生存期是固定的,存在于程序的整个运行过程。

全局变量不同的存储类别可以告诉我们其具体的作用域。

一般来说,外部变量是在函数的外部定义的全局变量,
它的作用域就是
从变量的定义处开始,
到本程序文件的末尾。
在这个作用域中,全局变量可以被程序中的各个函数所引用。

以下是对外部变量作用域扩展的几种情况:

1.在一个文件内扩展外部变量的作用域

在一些情况下,我们想要在外部变量定义以前的函数中引用该外部变量,那就需要在引用之前用关键字**“extern”**来对该变量进行“外部变量声明”,
他表示的是把该外部变量的作用域扩展到声明的位置。
这样,从声明处起,就可以使用该外部变量。

我们来看下面的程序:

#include <stdio.h>
int main()
   {
    int max();
    extern int A,B,C;
    printf("请输入三个整数:");
    scanf("%d %d %d",&A,&B,&C);
    printf("最大值是:%d\n",max());
    return 0;
   }

int A,B,C;

int max()
   {
    int m;
    m=A>B?A:B;
    if(C>m)
       m=C;
    return(m);
   }

这个程序的作用就是求三个整数中的最大值。

我们在程序的整体来看,A,B,C是在函数的外部定义的外部变量,常规来说,他的作用域就是从定义处开始到程序结束,也就是说上面的主函数是不可以用这三个变量的。

但是我们在主函数中使用了extern这个关键字,这样,外部变量的作用域就发生了扩展了,现在它们可以在extern声明处开始到程序结束。

另外,我们注意一下,A,B,C是在函数外部定义的,它们是外部变量,不需要进行参数传递,可以在被调用函数中直接调用。

其实,我们可以直接把外部变量的定义放在需要使用的所有函数的前面,这样我们就不需要再进行extern声明。

在使用extern进行声明时,变量的类型名是可以省略的,
extern的作用并不是定义变量,有没有类型名不重要。

extern int A;
extern A;

2.将外部变量的作用域扩展到其他文件

我们知道,一个C程序可以是由一个或多个源程序文件组成。
那么当程序由多个源程序文件组成时,如果想要在一个文件中引用另一个文件中已经定义的外部变量,有以下几点是需要注意的:
1.在两个文件中不能分别定义同一个名称的外部变量,这样会导致在进行程序的连接时会出现“重复定义”的错误。
2.在任意一个文件中定义一个外部变量,当我们需要在另一个文件中使用这个外部变量时就用extern在该文件中对该外部变量进行“外部变量声明”,即“extern 外部变量”。这样在编译和连接时,系统就会知道该外部变量有“外部链接”,就能自动从其他文件中找到该变量并引用,将其作用域扩展到本文件中。

下面我们来看一个例子:

文件1:

#include <stdio,h>
int A;
int main()
   {
    int power(int);
    int b=3,c,d,m;
    printf("enter the numer a and its power m:\n");
    scanf("%d,%d",&A,&m);
    c=A*b;
    printf("%d*%d=%d\n",A,b,c);
    d=power(m);
    printf("%d**%d=%d\n",A,m,d);
    return 0;
     }

文件2:

extern A;
int power(int n)
   {
    int i,y=1;
    for(i=1;i<=n;i++)
       y*=A;
    return(y);
    }

我们来运行一下:
在这里插入图片描述
在这里,我们需要注意。在VS中用extern int A;

其中的**代表的是幂次,
13××3代表的就是13的3次方。

在我们运用这种扩展方法的时候,我们需要知道外部变量是一个全局变量,我们在调用时可能会改变该全局变量的值,进而影响其他文件中的作用。

对于extern的作用,他可以扩展本文件中外部变量的作用域,也可以扩展外部文件中外部变量的作用域。
实际上,在运行时,如果遇到了extern就会先在本文件中找被扩展的外部变量,如果没有,就去其他文件中找。如果两者都找不到,就按照出错来处理。

3.将外部变量的作用域限制在本文件中

这个就跟前面的不一样了,前面的是扩展,这里是限制。

这个的完成需要的是static这么一个声明。

如:

文件1:
static int A;
int main()
   {
   ...
    }
文件2extern A;
void fun (int n)
   {
    ...
    A=A*n;
    ...
   }

在文件1中已经把外部变量A限制了,那么在文件2中就不能引用了,他的作用域已经被限制了。

这种加上了static的声明,只能在本文件中使用的外部变量叫做静态外部变量

使用static后,即使在同程序中的不同文件中使用相同的名字的外部变量就会互不相干。

相较于局部变量和全局变量,他们使用static是有区别的。

对于局部变量,其使用static就是把局部变量分配在静态存储区中,该变量在整个程序执行期间不释放,其所分配的空间始终存在。

对于全局变量,只是将变量的作用域限制在本文件中。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值