C存储类别

C 提供了多种不同模型的存储类别在内存中存储数据,从硬件方面看,被存储的每个值都占用一定的物理内存,C语言把这样的一块内存称为对象;可以用作用域,链接,存储期对对象进行描述,不同的存储类别具有不同的存储期,作用域和链接;

目录

一、存储类别的描述

1.作用域:

2.链接

3.存储期

二、五种存储类别

 1.自动变量

2.寄存器变量

3.块作用域的静态变量(局部静态变量)

4.外部链接的静态变量

5.内部链接的静态变量

三、多文件

四、函数的存储类别

五、按需知道


 

一、存储类别的描述

1.作用域:

作用域描述程序中可访问标识符的区域。一个C变量的作用域可分为块作用域函数作用域函数原型作用域文件作用域

1.1块作用域

用一对花括号括起来的代码区域叫做块,定义在块中的所有变量具有块作用域。需要注意的是,尽管函数形参的声明在函数左花括号之前,但他们也属于函数体这个快,即具有块作用域。

int pt0(int x,int va)
{
	int sum = va,x++;
}

在该例中,形参x,va以及在内层块中声明的变量sum的作用域都仅局限于函数pt0中,只有内层块的代码可以访问它们; 

在C99之前,具有块作用域的变量都必须在块的开头声明,而C99放宽了标准,允许在块的任意位置声明变量,同时把块的概念扩展到包括for循环,while循环, do while 循环和if语句所控制的代码,即使这些代码没有用花括号括起来也视为一种块。因此便有了如下情况       i被视为for循环块的一部分,一旦程序离开for循环就不能再访问i;

for (int i = 0; i < 100; i++)
{
	printf("%d",i);
}

1.2 函数作用域

函数作用域仅用于goto语句的标签。一个函数中的所有语句标签必须唯一。

1.3函数原型作用域

用于函数原型的形参名,形参的作用范围从定义处到函数原型声明结束,对于编译器来说,函数原型中的形参只关注其类型,形参名无关紧要,但是在变长数组中,数组后方括号中必须使用函数原型中已声明的名称。

void func(int x,int y,int array[x][y]);

1.4文件作用域

定义在函数外面的变量具有文件作用域,由于这样的变量可用于多个函数,所以文件作用域变量也被称为全局变量;

#include<stdio.h>
int file_x;
int dog(..);
int cat(..);
int main()
{
...
}
int dog(..)
{
...
}
int cat(..)
{
...
}

*翻译单元

编译器把所有源代码文件和头文件看作是一个包含信息的单独文件,被称为翻译单元,而描述一个具有文件作用域的变量时,它的实际范围是整个翻译单元。多个源文件代码组成多个翻译单元,每个翻译单元都对应一个源代码文件及其包含的文件。

2.链接

链接的概念适用于变量,C语言中的变量有3种链接属性:外部链接,内部链接或无链接

2.1 无链接

具有块作用域,函数作用域或函数原型作用域的变量都是无链接变量,这意味着这些变量属于定义它们的块,函数或函数原型私有。

2.2 外部或内部链接

具有文件作用域的变量可以是外部链接或内部链接,外部链接变量可以在具有多个翻译单元程序中使用,内部链接变量只能在一个翻译单元中使用。

另外常把具有内部链接的文件作用域称为文件作用域,把具有外部链接的文件作用域成为程序作用域或全局作用域,利用存储说明符static说明该变量为内部链接:static int dog=250;

3.存储期

作用域和链接描述的是标识符的可见性,即对应对象的可访问的范围,而存储器则描述了通过这些标识符访问的对象的生存期。C语言中的对象有四种存储期:静态存储期,线程存储期,自动存储期和动态分配存储期。

3.1 静态存储期

具有静态存储期的对象在程序的执行期间一直存在,无论内部链接还是外部链接,文件作用域的变量都具有静态存储期。

注意,对于文件作用域变量,关键字static表面了期链接属性而非存储期,需要与之后块作用域的静态变量声明时的static区别

3.2 线程存储期

用于并发程序设计,程序执行分为多个线程。具有该存储期的对象从被声明到线程结束一直存在。

3.3 自动存储期

具有块作用域的变量通常具有自动存储期,当程序进入定义这些变量的块时,为这些变量分配内存,退出时将分配的内存释放,变长数组的存储期从声明处到块的末尾而非块的开始到末尾。

具有块作用域的变量也能具有静态存储期,只需要在块中声明该变量时使用static说明符。

二、五种存储类别

C语言使用作用域,链接,存储期为变量定义了多种存储方案

ee87a43988554e7ab55d466155b324e0.png

 1.自动变量

属于自动存储类别的变量具有自动存储期,块作用域,无链接。默认情况下,声明在块或函数头中的任何变量都属于自动存储类别。为了更清楚的表达你的意图,可以显示使用关键字auto(存储类别说明符)。

 在嵌套块的情况下,如果内层块中声明的变量与外层块中的变量同名时,内层块会隐藏外层块的定义。但是离开外层块时,外层块变量的作用域又回到了原来的作用域。

1.1 没有花括号的块

前面提到的C99的新特性:作为循环或判断语句的一部分,即使不使用花括号,也是一个快。整个循环是他所在块的子块,循环体时整个循环块的子块,判断语句与此类似。这些规则会影响到声明的变量和这些变量的作用域,写新定义的同名变量会隐藏原始变量,到其所在块末尾时,该变量消失,被隐藏的原始变量又重新起作用。

1.2 自动变量的初始化

自动变量不会初始化,除非显式初始化它,未初始化的的自动变量的值时之前占用分配给它的空间中的任意值,可以用已被初始化的变量或常量为其初始化。

2.寄存器变量

变量通常存储在计算机内存中。寄存器变量存储在CPU的俱存其中,它是最快的可用内存。与普通变量相比,访问和处理这些变量的速度要更快;但是,由于其并非存储在内存中,因此无法获取其地址;另外,声明寄存器变量需要使用关键字register。除此些之外,在绝大多数方面,寄存器变量和自动变量一样,都是块作用域,无链接和自动存储器。

*声明register类别与直接命令相比更像是请求,编译器必须根据寄存器或最快可用内存的数量衡量你钓请求,或者直接忽略你的请求,在这种情况下,寄存器变量就直接变成自动变量,并且仍然不能取地址。

在函数头中使用register,便可请求形参是寄存器变量。

另外,可申请为寄存器变量的数据类型有限。例如,处理器中的寄存器可能没有足够大的空间来存储double类型。

3.块作用域的静态变量(局部静态变量)

静态指的是变量在内存中的位置,而非值。具有文件作用域的变量自动(必须)具有静态存储期。我们可以创建具有静态存储期,块作用域的局部变量,这些变量和自动变量一样,具有相同的作用域,但是程序离开它们所在的块后,这些变量不会消失。也就是这种变量具有块作用域,无链接,但是静态存储期而非自动存储期。计算机在多次函数调用之间会记录它们的值。

在块中(提供块作用域和无链接)以存储类别说明符static(提供静态存储期)来声明这种变量。

#include <stdio.h>
void trystat(void);

int main(void)
{
    int count;
    
    for (count = 1; count <= 3; count++)
    {
        printf("Here comes iteration %d:\n", count);
        trystat();
    }
    
    return 0;
}

void trystat(void)
{
    int fade = 1;
    static int stay = 1;
    
    printf("fade = %d and stay = %d\n", fade++, stay++);
}

trystat函数先打印再递增变量的值,三次调用中fade均为1,而stay由1增至3,说明静态变量stay保存了它被递增1后的值,即每次调用时fade都会被初始化一次,而静态变量stay只在编译该函数时被初始化一次。

另外,与寄存器变量不同的是,不能像使用register一样在函数形参中使用static。

4.外部链接的静态变量

外部链接的静态变量具有文件作用域,外部链接和静态存储期。该类别有时被称为外部存储类别,属于该类别的变量称为外部变量。

把变量的定义性声明放在所有函数的外面便创建了外部变量。当然,为了指出该函数使用了外部变量,可以在函数中用extern再次声明,在函数中再次声明数组时不用指明数组大小。如果一个源代码文件使用的外部变量定义在另一个源代码文件中,则必须使用extern在该文件中声明该变量。

在函数中创建与全局变量同名的局部变量时会隐藏原始全局变量,直到块结束,如果不得已要这样做,可以在局部变量声明时加上auto存储类别说明符表达此意图。

4.1 初始化外部变量

外部变量的初始化与自动变量类似,不同的是,如果未初始化外部变量,它们会被自动初始化为0,数组也一样,另外,不能用非常量表达式初始化外部变量。

4.2 外部名称

外部变量名比局部变量名的规则严格,因为外部变量名还要遵循局部环境规制,所受限制更多。

4.3 定义和声明

int dog=250;
int main()
{
    extern int dog;
}

dog被声明了两次,第一次为变量预留了存储空间,被称为定义式声明,第二次声明只告诉编译器使用之前已创建的dog变量,所以这不是定义,而是告诉编译器去别处查询其定义,被称为引用式声明。如果用extern 创建外部定义,编译器会假设该变量的定义在别处,而不会为其分配存储空间,因此,只用extern来引用现有的外部定义就行了。

5.内部链接的静态变量

该存储类别的变量具有静态存储期,文件作用域和内部链接。与外部变量一样在所有函数外面声明,不同点是需要使用static关键字。

外部变量可用于同一程序任意文件(全部翻译单元)的函数,而内部链接的静态变量只能用与同一个文件中的函数。

内部链接的静态变量同样可以用extern 且不会改变其连接属性。

int dog=250;
static int cat=250;
int main()
{
    extern int dog;
    extern int cat;
}

对于该程序所在的翻译单元,cat dog都有文件作用域,但是只有dog可以用于其他翻译单元。main()中两个引用式声明都说明两个定义的变量在别处,且不会改变二者的链接类型。

三、多文件

只有程序由多个翻译单元组成时,才能清楚理解到区别内部链接和外部链接的重要性。复杂的C程序通常由多个单独的源代码文件组成。有时多个文件要共用一个外部变量。C通过在一个文件中进行定义式声明,然后在其他文件中进行引用式声明来实现共享。也就是说,如果外部变量的定义在一个文件中(它只能在定义式声明时初始化),只是单方面允许其他文件使用它,其他文件在使用extern声明之前不能直接使用它。

四、函数的存储类别

函数也有存储类别,可以是默认的外部函数或静态函数,C99新增了内联函数。

假设以下函数在同一个程序中

int dog(void);
static dogman(void);
extern cat(void);

在同一个程序中,其他文件可以调用dog 和cat,但不能调用dogman,因为static存储类别说明符创建的函数属于该文件私有,这样做避免了名称冲突问题,由于dogman受限,所以在其他函数中可以使用与之同名的函数。

通常用extern声明定义咋其他文件中的函数。除了使用static的函数都默认为extern。

五、按需知道

尽量在函数内部解决该函数的任务,只共享那些需要共享的变量。

 

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
存储类别主要包括自动存储类别(auto)、静态存储类别(static)、动态分配存储类别(dynamic allocation)和寄存器存储类别(register)。 1.自动存储类别(auto): 自动存储类别通常只在函数内部使用,其变量被存储在栈中。该变量在程序开始执行时,分配内存,在函数执行完后,这个分配在栈中的内存就会被释放。 2.静态存储类别(static): 静态存储类别中的变量在程序开始执行时便已经分配内存,在整个程序执行期间,这个内存块一直存在,直到整个程序退出。定义静态变量的场合与auto变量一样也是在函数内部定义的,但是它是在函数执行之前便已经分配内存。静态变量只能在定义它的函数内访问,但是它的值在下一次函数调用时仍然保持不变。 3.动态分配存储类别(dynamic allocation): 动态分配的内存是位于堆(heap)中的,和栈区和静态存储区是不同的,这个内存块的大小可以在运行时通过调用诸如malloc()函数动态分配内存,当不再需要它的时候使用free()函数进行释放。 4.寄存器存储类别(register): 寄存器存储类别中的变量通常指针或者计数器等很小的变量,就是告诉编译器要尽量将其存放在CPU的寄存器中,而不是内存中,这样可以加快程序运行速度。 总的来说,内存存储的分主要是根据程序对存储空间的需求和使用次数等要素进行分配。程序员需要在写程序时根据实际需求选择不同的存储类别
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

vⅤ_Leon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值