C++: 内存模型——搞清楚全局变量、静态变量、const常量

一、前言

    相对于从名称定义全局变量、静态变量、const常量去了解他们,我们不如从本质上去区分他们。也就是从另一个角度看待这些变量或常量的区别。

    对于C++中的变量而言,它有三种特性,存储持续性、作用域、以及链接性。

    其中存储连续性描述的是变量的生命周期,作用域和连接性描述的是变量的可见和可使用的范围,作用域一般针对于文件内部而言,链接性则是针对于文件与文件之间而言。

    显然,变量的生命周期和变量的可使用范围是有关系的,所以这就是我们在区分他们名称的时候会有困惑的原因。

    另外,为了更好的了解下面的内容,这里先介绍一下C++的单定义规则(One Definition Rule), 该规则指出,变量只能有一次定义,一种是定义声明(defining declaration),简称定义,他给变量分配存储空间,另外一种使引用声明(referencing declaration),简称声明,它不给变量存储空间,因为它引用已有的变量。

二、存储持续性

    类型:

        (1)自动存储持续性: 在函数定义中声明的变量-包括函数参数-具有自动存储持续性。 它们是在程序执行进入它们的函数或块时创建的 当执行离开函数或块时,为它们使用的内存被释放。 其中C++有两种自动存储持续性的变量。

        (2)静态存储持续性: 在函数定义之外或使用关键字static定义的变量具有静态存储持续时间。 它们一直持续到程序运行的整个时间。 C++有三种静态持续性变量。

        (3)线程存储持续性:如果变量使用了关键字thread_local,那么它的声明周期就跟线程的声明周期一样长。

        (4)动态存储持续性:用new运算发分配的内存一直存在,知道使用delete运算符将其释放或者程序结束为止。

    我们接触到最多的就是通过static关键字来定义变量的静态存储持续性,也就是使用了static关键字的变量,他们的生命周期将持续到程序结束。但值得注意的是,static关键字不仅仅改变了变量的存储持续性,也能影响链接性,详细的介绍见下。

三、作用域

    作用域(scope)描述了名称在文件(也称为翻译单元,因为各文件是单独编译的)的作用范围。

    类型:

        (1) 局部作用域:只在定义它的代码块中可用。

        (2)全局作用域,在定义位置到文件结尾都可用。

    所以我们说全局变量、局部变量,描述的是作用域。一般来讲,在没有添加static关键字的情形下,全局变量具有静态存储持续性,而局部变量具有自动存储持续性。当人为添加static关键字后,无论是全局变量还是局部变量,都具有静态存储持续性。

四、链接性

    我们刚刚说到,static关键字不仅影响存储持续性,此外它还跟const一起会影响链接性,它们将变量的链接性分成了三类。

    类型: 

        (1)外部链接性,可以被其他文件使用,持有该特性的变量:不加static且不加const的的全局变量

          外部链接如何使用:         

// a.cpp
int global = 1;                //定义声明,简称定义

//b.cpp
extern int global;             //引用声明,简称声明

           在a.cpp中我们定义了全局变量global,根据其链接性我们可以在b中使用它,但是要加上extern表示该变量来自外部文件,实际上global变量在内存中只有一个。

           同时不能做赋值操作,只能是声明,如果b.cpp中语句为extern int global=1,那么则会再定义一个global变量,为其分配内存空间,此时,名字为global的变量在内存中有两个,一个在a中,一个在b中。

        (2)内部链接性,只能在当前文件使用,持有该特性的变量:加了static的全局变量,或者加了const的全局变量

        (3)无链接性,   只能带当前文件的局部使用,持有该特性的变量:加了static的局部变量

    const关键字也能使全局变量变成内部链接性的变量(这是在C++中加上去的),如果我们想让const类型的全局变量链接性变成外部的,那么可以加入extern关键字,下面的例子就是a定义外部链接性的const常量states,b则是通过外部链接引用了a中的states。(static 前不能这样加extern实现这样的效果,会报错)

//a.cpp
extern const int states=50;           //定义声明,简称定义


//b.cpp
extern const int states;              //引用声明,简称声明

五、汇总

声明方式持续性作用域链接性存储描述
在代码块中自动局部(代码块)-自动
在代码块中,使用static静态局部(代码块)静态,无链接性
不在任何函数内静态全局(文件)外部静态,外部链接性
不再任何函数内,使用static静态全局(文件)内部静态,内部链接性
不再任何函数内,使用const静态全局(文件)内部静态,内部链接性

六、其他说明符和限定符

    在上面我们了解了了static说明符和extern说明符, 下面看看其他常用的说明符

说明符和限定符作用
auto表示自动类型推断 auto a = 1.414;, 这时auto相当于float
register指示系统尽可能在寄存器中存储该变量, register int a = 1;
static指示静态存储持续性,并指示全局变量只能内部链接
extern表明是引用声明,即声明引用在其他地方定义的变量
thread_local指出变量的持续性于其所属线程的持续性相同
const (限定符)他表明内存被初始化后,程序便不能再对他进行修改。并指示全局变量只能内部链接。
volatile(限定符)跟java中一样,表明该变量可能会被两个线程修改或使用,volatile是易变和不稳定的意思。我们知道,如果程序在几条语句两次使用了某个变量的值,则编译器可能不是让程序查找这个值两次,而是将这个值缓存到寄存器(做了优化)。这种优化假设变量的值在这两次使用之间不会变化。而现实是有可能另外一个线程或者进程已经把它修改了,所以volatile就是告诉编译器不要做这种优化。
mutable它指示即使结构或者类变量为const,其某个成员可以被修改,用来修饰可以被修改的成员。

七、static的在类中的使用

    在类中我们一般对类的成员函数加上static关键字,说明该函数时静态函数,它实际上也是指示静态存储持续性,就是这个类函数的生命周期等同于程序的生命周期,即使不创建类对象,只要加载了类,就可以对该函数进行访问。(当然,但我们对成员变量加上static的时候,也是一样的效果)。

    同时需要注意的是static的函数只能访问static的变量或函数,因为其他非static的成员隶属于实例化了的类对象,当对象不存在时,这些成员也没有被分配内存,所以就设置让static的函数不能访问非static的类成员。

八、参考资料

    C++ primer plus

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值