C语言学习——函数(变量的存储类别)

目录

8.9变量存储的类别

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

auto变量

用static声明局部变量

register变量

用extern声明外部变量

用static声明外部变量

关于变量的声明和定义


8.9变量存储的类别

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

前面我们已经学过了。从变量的作用域(即从空间)角度来分,可以分为全局变量局部变量

可以从另一个角度,从变量值存在的时间(即生存期)角度来分,可以分为静态存储方式动态存储方式

静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式。

动态存储方式是指在程序运行期间根据需要进行动态的分配空间的方式。

首先,我们可以了解一下内存中供用户使用的存储空间的情况。存储空间可分为三部分。

(1)程序区;

(2)静态存储区;

(3)动态存储区。

数据分别存放在静态存储区动态存储区中。全局变量全部存放在静态存储区中,在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不是动态地进行分配和释放。

在动态存储区中存放以下数据:

  • 函数形式参数。在调用函数时给形参分配存储空间,

  • 自动变量(为加static生命的局部变量)。

  • 函数调用时的现场保护和返回地址。

对以上的这些数据,在函数调用开始时分配动态存储空间,函数结束时释放这些空间。在程序执行过程中,这种分配和释放是动态的,如果在一个程序中调用两次同一函数,分配给此函数中局部变量的存储空间地址可能是不相同的。如果在一个程序包含若干个函数,每个函数中的局部变量的生存期并不等于整个程序的执行周期,它只是程序执行周期的一部分。根据函数调用的需要,动态地分配和释放存储空间。

在C语言中,每一个变量和函数有两个属性:数据类型数据的存储类别。对数据类型,如整型/字符型。存储类别指的是数据在内存中的存储方式。存储方式分为两大类:静态存储类动态存储类。具体包括四种:

(1)自动的(auto)

(2)静态的(static)

(3)寄存器的(register)

(4)外部的(extern)

根据变量的存储类别,可以知道变量的作用域和生存期。下面分别作介绍:

auto变量

函数中的局部变量,如果不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。局部变量称为自动变量。自动变量用关键字auto作存储类别的声明。例如:

int f(int a)  //定义f函数 a为形参
{
    auto int b,c=3;  //定义b、c为自动变量
}

其中,a是形参,b、c是自动变量,对c赋初值3,。执行完f函数后,自动释放a、b、c所占的存储单元。

实际上,关键字“auto”可以省略,auto不写则隐含确定为“自动存储类别”,它属于动态存储方式。程序中大多数变量都是属于自动变量。前面介绍的函数中定义的变量都没有声明为auto,其实都隐含指定为自动变量。例如:

auto int b,c=3;
 //与
int b,c=3;
//二者等价
用static声明局部变量

有时候希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即其占用的存储单元不释放,在下一次该函数调用时,改变了已有值,就是上一次函数调用结束时的值。这时就应该指定局部变量为“静态局部变量”,用关键字static进行声明。下面是一个简单的例子:

//考察静态局部变量的值
#include<stdio.h>
 
int main()
{
    int f(int);  //函数声明 
    int a=2;
    for(int i=0;i<3;i++)
    {
        printf("%d ",f(a)); //将循环里的 0 1 2 分别传给函数f 
    }
    return 0;
 } 
 
int f(int a)
{
    auto int b=0;  //自动变量 函数调用结束时释放内存空间 即每次调用函数b都为0 
    static int c=3;   //静态局部变量 调用结束后保留原值 存储单元不释放 c的值会保留上一次调用结束后的值 
    b = b+1;
    c = c+1;  //第一次 c=4;第二次 c=5;第三次c=6; 
    return (a+b+c);  //  0+1+4  1+1+5  2+1+6 
}
​
//输出
7 8 9 

对静态函数变量的说明:

  • 静态局部变量属于静态类别存储,在静态存储区内分配内存单元。在程序整个运行期间都不释放。而自动变量(即动态局部变量)数据动态存储类别,占动态存储区空间而不占静态存储区空间,函数调用结束后释放。

  • 对静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。以后每次调用函数时不在重新赋初值而只是保留上次函数调用结束时的值。而对自动变量赋初值,不是在编译时进行的,而是在函数调用时进行的,每调用一次函数重新给一次初值,相当于执行一次赋值语句。

  • 如在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初始值0(对数值型变量)或空字符(对字符型变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。这是由于每次函数调用结束后存储单元已释放,下次调用时又重新另分配存储单元,而所分配的单元中的值是不确定的。

  • 虽然静态局部变量在函数调用结束后仍存在,但是其他函数时不能引用它的。

需要用局部静态变量的情况如下:

(1)需要保留函数上一次调用结束时的值。例如可以用下面的方法求n!。

//输出1到5的阶乘 
#include<stdio.h>
int main()
{
    int fac(int n);
    for(int 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;   //每次调用fac(i),输出一个i!,同时保留这个i!的值以便下次再乘(i+1)
    return f;
}
​
//输出:
1!=1
2!=2
3!=6
4!=24
5!=120 

(3)如果初始化后,变量只被引用而不改变其值,则这时用静态局部变量比较方便,以免每次调用时重新赋值。

但是我们可以看到,用静态存储变量要多占内存(长期占用不释放,而不能像动态存储那样一个存储单元可供多个变量使用,节约内存),而且降低了程序的可读性,当调用多次的的时候往往弄不清静态局部变量的当前值是什么。因此,非必要时,不要多用静态局部变量。

register变量

一般情况下,变量(包括静态存储方式和动态存储方式)的值是存放在内存中的。当程序中用到哪一个变量的值时,由控制器发出指令将内存中该变量的值送到运算器中。经过运算器进行运算,如果需要存数,再从运算器将数据送到内存存放

如果有一些变量使用频繁(例如,在一个函数中执行10000次循环,每次循环中都要引用某些局部变量),则为存取变量的值要花费不少时间。为提高执行效率,C语言允许将局部变量的值放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中去存取。由于寄存器的存取速度远高于对内存的存取速度,因此这样做可以提高执行效率。这种变量叫做寄存器变量,用关键字register作声明。

使用寄存器变量,输出1~n阶乘的值。

//输出1到5的阶乘 
#include<stdio.h>
int main()
{
    int fac(long);
    long i,n;
    scanf("%ld",&n);
    for(i=1;i<=n;i++)
    {
        printf("%ld!=%ld\n",i,fac(i));
    }
    return 0;
}
​
int fac(long n)
{
    register long i, f=1;  //定义寄存器变量 如果n的值大,则能节约许多执行时间
    for(i=1;i<=n;i++)
    {
        f = f*i;
    }
    return f;
}

说明:

  • 只有局部自动变量和形式参数可以作为寄存器的变量,其他(如全局变量)不行。在调用一个函数时占用一些寄存器以存放寄存器变量的值,函数调用结束释放寄存器。此后,在调用另一个函数时又可以利用它来存放该函数的寄存器变量。

  • 一个计算机系统中的寄存器数目是有限的,不能定义多个寄存器变量。

  • 局部静态变量不能定义为寄存器变量。

  • register static int a,b,c;  //这种写法是错误的

    不能把变量a、b、c既放在静态存储区中,又放在寄存器中,二者只能居其一。对一个变量只能声明为一种存储类别。

当今的优化编译系统能够识别使用频繁的变量,从而自动地将这些变量存放在寄存器中,而不需要程序设计者指定。因此,实际上用register声明变量是不必要的。但是我们需要有一定的了解,以便阅读他人的代码。

用extern声明外部变量

外部变量是函数的外部定义的全局变量,它的作用域是从变量的定义处开始,到本程序文件的末尾。在此作用域内,全局变量可以为程序中各个函数所引用。编译时将外部变量分配在静态存储区。

有时需要用extern来声明外部变量,以扩展外部变量的作用域。

在一个文件声明外部变量

如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件结束。如果在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字extern对该变量作“外部变量声明”,表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。例如:

用extern声明外部变量,扩展它在程序文件中的作用域

#include<stdio.h>
​
int main()
{
    int max(int,int);
    extern int A,B;   //外部变量声明  表示A和B是已经定义的外部变量(但定义的位置在后面)这样main函数中就可以合法使用了 
    printf("%d\n",max(A,B)); 
    return 0;
 } 
​
int A=13,B=-8;  //定义外部变量  定义在main之后 所以main中不可使用 我们可以用extern进行声明 
int max(int x,int y)  //定义max函数 
{
    int z;
    z=x>y?x:y;
    return (z);
 }
 
//输出:13 

外部变量定义和外部变量声明的含义是不同的外部变量的定义只能有一次,它的位置在所有函数之外,而同一文件中的外部变量的声明可以有多次,它的位置可以在函数之内(哪个函数要用就再哪个函数中声明),也可以在函数之外(在外部变量的定义点之前)。系统会根据外部变量的定义(而不是根据外部变量的声明)分配存储单元。

用static声明外部变量

有时在程序设计中希望某些外部变量只限于被本文件所引用,而不能被其他文件引用。这时可以在定义外部变量时加一个static声明。例如:

//file1.c
static int A;
int main()
{
    //... ... 
}
​
//file2.c
extern int A;
int fun(int n)
{
    //... ... 
    A = A*n;
}

在file1.c中定义了一个全局变量A,但它用static声明,因此只能用于本文件,虽然在file2.c文件中用了“extern int A;”,但 “file2.c文件中无法使用 file1.c中的全局变量 A。

这种加上static声明,只能用于本文件的外部变量称为静态外部变量。在程序设计中,常由若干人分别完成各个模块,各人可以独立地在其设计的文件中使用相同的外部变量名而互不相干。只需在每个文件中的外部变量前加上 static 即可。这就为程序的模块化、通用性提供方便。如果其他文件不需要引用本文件的外部变量,可以对本文件中的外部变量都加上static,成为静态外部变量,以免被其他文件误用。

需要指出: 不要误认为对外部变量加static声明后才是静态存储方式(存放在静态存储区中),而不加static的是动态存储(存放在动态存储区)。两种形式的外部变量都是静态存储方式(存放在静态存储区),只是作用范围不同而已,都是在编译时分配内存的。

关于变量的声明和定义

用static来声明一个变量的作用有二:

  • 对局部变量用static声明,则使该变量在整个程序期间不释放,为其分配的空间始终存在。

  • 全局变量用static声明,则该变量的作用域只限于本文件模块(即被声明的文件中)。

声明:本文章为个人学习笔记,资料整理参考谭浩强《C程序设计(第三版)》如有错误,欢迎大家指正!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值