C++学习笔记(10)——内存模型

在《C++学习笔记5——C++复合类型之指针》中,在分析new的时候,总结了内存分配方法。当时,根据C++管理内存数据的方式将数据存储方式总结为自动存储、静态存储、动态存储。本篇笔记将在此基础上,深入探讨C++中内存模型的持续性、链接性和作用域。在学习完函数之后,总结这些知识有助于合理规划自己的程序结构。毕竟,稍微有点规模的程序都不可能只在一个文件或几个函数内完成。

1.什么是持续性、链接性和作用域?

作用域是空间上的概念。也即变量作用的区域。从它作用的区域大体可分为两种:局部和全局变量。

从字面意思理解,持续性是时间上的一个概念。即变量在内存中存在的时间周期,也即何时分配内存,何时释放内存。根据之前学习总结的自动存储、静态存储、动态存储方式,可以得出变量的三种持续性:

自动:在函数内声明的函数参数和变量的存储性为自动,作用域为局部,即在作用域之外自动释放内存。

静态:静态变量即用static修饰的变量,其存在于整个程序运行过程中。

动态:使用new创建的变量,需要用delete释放。

链接性表示的是多个文件(姑且以文件来作为分界,准确讲叫翻译单元)之间的变量共享方式。具体可分为三种:

无链接性:自动变量的名称没有链接性,它们不能共享。

内部链接性:链接性为内部的名称只能在一个文件中的函数共享

外部链接性:链接性为外部的名称可在文件间共享。

2.三者之间的关系

变量的持续性、链接性和作用域往往有着内在的联系。可以通过一张简单的表格来显示它:

存储描述

持续性

作用域

链接性

位置

自动

自动

局部

无链接性

代码块内声明的变量

寄存器

自动

局部

无链接性

代码块内,用register关键字

静态无链接性

静态

局部

无链接性

代码块内,并使用static声明

静态内部链接性

静态

文件全局

内部链接性

不在任何函数内,用static声明

静态外部链接性

静态

文件全局

外部链接性

不在任何函数内

动态

动态

 

 

new创建的变量

注意:

未被初始化的静态变量,在编译时被自动初始化为0;

只能用常量表达式来初始化静态变量。包括字面值常量,const常量,enum常量和sizeof操作符。

外部变量与自动变量同名时,局部变量与全局变量同名时,新定义的变量暂时会隐藏旧定义。

不同文件间的同名的全局静态变量相互覆盖(自己隐藏其他文件)。

静态局部变量只进行一次初始化,再次调用该函数时将不再初始化。如在add函数中定义一个static int aa=1;之后执行aa++;第一次调用时,该变量被初始化为1,aa值为2,第二次调用该函数时,不会再初始化,aa值将为2,执行完后为3.但它的作用域是在add内,在main中无法访问该变量,也即无链接性。

3.单定义规则与外部共享变量

C++规定,变量只能有一次定义。为满足这种需求,C++可以对变量进行定义和引用声明,定义即分配内存空间,引用声明不给变量分配空间,它引用已有的变量。引用声明要用extern关键字修饰,且不进行初始化。

因此,基于单定义原则,要在多个文件中共享一个变量,需要在一个文件中定义这个变量,在使用这个变量的其他文件中,用extern关键字声明它。

一种典型的应用就是常见的全局变量使用方法。我们创建一个GDfine.cpp文件,在该文件中定义一个变量int g_var,同时也可以对它进行初始化:int g_var = 1;然后定义一个GDefine.h文件,在该文件中引用声明它,即extern int g_var;这样,在其他文件使用该全局变量就可以直接用预编译头#include GDefine.h,如,在main.cpp中,就可以直接用该g_var变量了。该变量的持续性为静态,即在整个程序运行过程中都存在,链接性为外部,作用域为全局。也即在main中操作该变量,如g_var++,则g_var将在全局范围内发生变化。

全局变量可以方便多个文件甚至多个线程共享同一个变量,也成为了函数间、线程间通信的一种方式。但并不是说全局变量就可以滥用,因为易于访问的代价很大——程序不可靠。计算经验表明,程序越能避免对数据进行不必要的访问,越能保持数据的完整性,则程序越可靠。所以,通常情况下,应尽量使用局部变量,不应不加区分的使用全局变量。

4.说明符和限定符

说明符包括

auto(旧版本C++的auto表明变量是自动的,但很少有人用。在C++11 中不再是说明符,而成为了类型)

register(寄存器变量,表明该变量的使用频率较高,C++11取消了这个关键字)

static(静态变量)

extern(应用声明)

thread_local(C++11新增线程静态变量,相当于对于程序来说的静态变量)

mutable(修饰结构和类中的成员,使得结构或类为const时,其成员可修改)

cv限定符包括:

const:默认情况下全局变量的链接性是外部的,但const修饰的全局变量是内部的,也就是说在文件一开始定义const int a=10;这相当于static const int a=10;这是为了方便将常量可以放在.h中供多个文件使用,不然全局共享常量据需要extern关键字了,这显得有点繁琐。当然,这还意味着,每个文件都将有自己的一套常量,而不是共享一组常量。如果你觉得这样理解起来很困难,你也可以用extern关键字声明它,这样由于有const,其值不能改变,外部链接性对于const变量来说没有实际意义。

volatile:表明即使代码没有对某内存单元进行修改,但其值可能发生变化。即该变量是易变的,不稳定的。如硬件修改了该变量的情况。这个限定符主要用来优化编译器能力。

5.函数和语言的链接性

和变量一样,函数也有链接性。C++不允许在一个函数内定义另外一个函数,因此函数的持续性都是静态的,也即在整个程序执行期间都存在。默认情况下,函数的链接性是外部饿,也即可以在文件间共享。当然通常可以用extern来表明其来自于另一个文件中的定义。能被共享的前提就是该文件被编译。

语言也有链接性。链接程序要求每个函数都有不同的符号名。C语言通常将函数名翻译为_func,这被称为C语言链接特性。C++由于函数重载等问题,一个函数名可能对应多个函数,则翻译规则比较复杂,如void func(double, double)被翻译为_func_d_d,这种被称为C++语言链接特性。

这就带来一个问题,我们在C++程序中用C语言写了一个函数,编译器将默认用C++链接性来翻译函数名,比如我们用到了C语言中的库函数printf,链接时被翻译为_printf_i,但实际上库是_printf,这就会出现链接不成功的问题。可以用函数原型来显式的规定函数的链接性:

extern “C”void func();//C语言链接性

extern “C++” void func();//c++语言链接性

extern void func();//c++语言链接性

因此,解决上述问题的方法是,在C语言编写的函数原型前加上extern “C”即可。常见的,用C语言封装的类或DLL都将用到这个链接性声明。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

bjtuwayne

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

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

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

打赏作者

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

抵扣说明:

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

余额充值