C语言:存储类别、链接和内存管理

C语言:存储类别、链接和内存管理

变量:

1、存储时期:变量在内存中保留的时间

2、变量的作用域和链接:一起表明程序的哪些部分可以通过变量名来使用该变量

不同的存储类型提供了变量的作用域、链接以及存储时期的不同组合。

多个不同的源文件共享的变量、某个特定文件中的所有函数都可以使用的变量、只有在某个特定函数中才可以使用的变量、只有某个函数的一小部分内可以使用的变量

作用域:代码块作用域、函数原型作用域或者文件作用域

文件作用域(file scope):一个在所有函数之外定义的变量。具有文件作用域的变量从它定义处到包含该定义的文件结尾处都是可见的。

    #include <stdio.h>
    int uints=0;
    void critic(void);
    int main(void)
    {
        ...
    }
    void critic(void)
    {
        ...
    }

变量uints具有文件作用域,在main()和critic()中都可以使用它。因为它们可以在不止一个函数中使用,文件作用域变量也被称为 全局变量(global variable)。

链接

外部链接(external linkage)、 内部链接(internal linkage)、 空链接(no linkage)

具有代码块作用域或者函数原型作用域的变量有空链接,意味着它们是由其定义所在的代码块或函数原型所私有的。

具有文件作用域的变量可能有内部或者外部链接。一个具有外部链接的变量可以在一个多文件程序的任何地方使用。一个具有内部链接的变量可以在一个文件的任何地方使用。

通过观察在外部定义中是否使用了存储类说明符static可以知道一个文件作用域具有内部链接还是外部链接。



    int giants=5;           //文件作用域,外部链接
    static int dodgers=3;   //文件作用域,内部链接
    int main(void)
    {
        ...
    }

和该文件属于同一程序的其他文件可以使用变量giants。变量dodgers是该文件私有的,但是可以被该文件中任一函数使用。

存储时期

静态存储时期(static storage duration)和 自动存储时期(automatic storage duration)

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

具有自动存储时期的变量在程序进入定义这些变量的代码块时,将为这些变量分配内存;当退出这个代码块时,分配的内存将被释放。具有代码块作用域的变量一般情况下具有自动存储时期。

作用域链接存储时期
代码块作用域空链接自动存储时期
函数原型作用域空链接自动存储时期
文件作用域内部链接/外部链接静态存储时期

C使用作用域、链接和存储时期来定义5中类型:自动、寄存器、具有代码块作用域的静态、具有外部链接的静态,以及具有内部链接的静态。

备注 存储类型

存储类别存储期作用域链接声明方式
自动自动块内
寄存器自动块内,使用关键字register
静态外部链接静态文件外部所有函数外
静态内部链接静态文件内部所有函数外,使用关键字static
静态无链接静态块内,使用关键字static

1、自动变量

在内层代码块定义的名字是内层代码块所使用的变量。我们称之为内层定义覆盖(hide)了外部定义,但当运行离开内层代码块时,外部变量重新恢复作用。

在程序执行代码块内语句时,代码块作用域的变量覆盖了具有文件作用域的同名变量。

2、寄存器变量

通常,变量存储在计算机内存中。寄存器变量可以被存储在CPU寄存器中,或更一般地,存储在速度最快的可用内存中,从而可以比普通变量更快地被访问和操作。

因为寄存器变量多是存放在一个寄存器而非内存中,所以无法获得寄存器变量的地址。

3、具有代码块作用域的静态变量/内部静态存储类

具有文件作用域的变量自动(也是必须的)具有静态存储时期。也可以创建具有代码块作用域,兼具静态存储的局部变量。这些变量和自动变量具有相同的作用域,但当包含这些变量的函数完成工作时,它们并不消失。也就是说,这些变量具有代码块作用域、空链接,却有静态存储时期。从一次函数调用到下一次调用,计算机都记录它们的值。这样的变量通过使用存储类说明符static(这提供了静态存储时期)在代码块内声明(这提供了代码块作用域和空链接)创建。

4、具有外部链接的静态变量

具有外部链接的静态变量具有文件作用域、外部链接和静态存储时期。这一类型有时被称为外部存储类(external storage class),这一类型的变量被称为外部变量(external varialbe)。 把变量的定义声明放在所有函数之外,即创建了一个外部变量。为了使程序更加清晰,可以在外部变量的函数中通过使用extern关键字再次声明它。如果变量是在别的文件中定义的,使用extern来声明该变量就是必须的。

外部变量的作用域:从声明的位置开始到文件结尾位置。也说明了外部变量的生存期。

1、外部变量初始化

和自动变量一样,外部变量可以被显式地初始化。 不同于自动变量的是,如果您不对外部变量进行初始化,它们将自动被赋初值0。这一原则也适用于外部定义的数组元素。不同于自动变量,只可以用常量表达式来初始化文件作用域变量。

int tern=1;              //定义tern
int main(void)
{
    extern int tern;    //使用在其他地方定义的tern变量
    ......
}

const类型限定符

以const类型关键字声明的对象,其值不能通过赋值来递增,递减来修改。

在形参和指针声明中使用const

  • 创建了pf指向的值不能被改变,而pf本身的值可以改变。例如,可以设置该指针指向其它const值
const float *pf;//pf指向一个float类型的const值
1
  • 创建的指针pt本身不能改变,pt必须指向同一地址,但是它所指向的值可以改变。
float * const pt;//pt是一个const指针
1
  • 表明ptr既不能指向别处,它所指向的值也不能改变。
const * float * const ptr;
1

把const放在类型名后,之前,说明该指针不能改变它所指向的值。
简而言之,const放在
左侧的任意位置,限定了指针指向的数据不能改变;
const放在*的右侧,限定了指针本身不能改变

对全局数据使用const

使用全局变量是一种冒险的方法,因为这样做暴露了数据,程序的任何部分都能更改数据,但如果把数据设置为const,就可以避免这样的危险,因此使用const限定符声明全局变量很合理,然而,在文件间共享const要小心,可以采用两个策略:

  • 在一个文件中采用定义式声明,另一个文件采用引用式声明:
/*file1.c---定义了一些外部const变量*/
const double PI = 3.1415926;
const char *MONTHS [12] = {1,2,3,4,5,6,7,8,9,10,11,12};

/*file2.c---使用定义在别处的外部const变量*/
extern const double PI;
extern const char *MONTHS[];
1234567
  • 把const变量放在另一个头文件中,然后在其它头文件中包含该头文件:
/*constant.h--定义了一些外部const变量*/
static const double PI = 3.1415926;
static const char * MONTHS = {1,2,3,4,5,6,7,8,9,10,11,12};

/*file1.c---使用定义在别处的外部const变量*/
#include "constant.h"

/*file2.c---使用定义在别处的外部const变量*/
#include "constant.h"
123456789

restrict类型限定符(C99)

restrict关键字允许编译器优化某部分代码以更好的支持计算。它只能用于指针,表明该指针是访问数据对象的唯一且初始的方法。

int ar[10];
int * restrict restar = (int *) malloc (10 * sizeof(int));
int *par = ar;
123

这里,指针restar是访问由malloc()所分配内存的唯一且初始的方式。因此,可以用restrict关键字限定它。而指针par既不是访问ar数组中数据的初始方式,也不是唯一方式。所以不用把它设置为restrict.

restrict关键字有两个读者。一个是编译器,该关键字告知编译器可以自由假定一些优化方案。另一个读者是用户,该关键字告知用户要使用满足restrict要求的参数。总而言之,编译器不会检查用户是否遵循这一限制,但是无视它后果自负。

_Atomic类型限定符(C11)

并发程序设计把程序执行分成可以同时执行的多个线程。这个程序设计带来了新的挑战,包括如何管理访问相同数据的不同线程。C11通过包含可选的头文件stdatomic.h和threads.h ,提供了一些可选的(不是必须实现的)管理方法。值得注意的是,要通过各种宏函数来访问院子类型。当一个线程对一个原子类型的对象执行原子操作时,其他线程不能访问该队形。例如,下面的代码:

int hogs; // 普通声明
hogs = 12; // 普通赋值
// 替换为
_Atomic int hogs; // hogs是一个原子类型的变量
atomic_store(&hogs, 12); // stdatomic.h 中的宏
12345

这里,在hogs中储存12是一个原子过程,其他线程不能访问hogs。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值