内存管理对于程序设计的重要性不言而喻,这段时间重温了一下C,C++,ObjC的内存管理机制,并做了横向对比,下面对最近所学做个学习笔记,如有纰漏谬误,有请指正。
参考书籍:C Primer,C++ primer plus,Programming in Object-C,Effective-C 2.0,其他的个别博客及问答。
C与C++
C语言和C++语言的内存管理差别不大,应该说C++内存管理是C的超集,这里就做个统一的介绍,个别不一样的会指出来。
内存管理顾名思义就是对于程序运行时对程序使用计算机内存的原则性,高效性的管理,使得程序得以正确运行,高效运行。
接下来是关于内存管理的几个概念。
作用域
C与C++的变量有作用域这一规则,作用域即变量在代码中何处起作用,何处不起作用,按照这个规则,可以分为以下三种作用域:
全局,文本,局部
也称外部链接,内部链接和空链接。
全局变量不仅在文本中任何一个角落可以使用,而且可以跨文本使用;
文本变量则在当前文本中的任何地方可以使用,不可以跨文本使用;
局部变量仅限于代码块使用,即其声明开始,到声明所在代码块结束。
存储时期
存储时期即变量在程序运行时,哪个时间段起作用哪个时间段不存在,可以分为以下三类:
静态存储(static extern),自动存储(auto),动态存储
其构成了变量在时间轴上的作用区间。
静态存储在程序编译时编译器便在内存中为其开辟了存储空间,程序运行过程中一直存在,直至程序结束;
自动存储在某个代码块中存在,当代码块结束时,系统自动回收其内存空间(也称内存堆栈);
动态存储由程序在运行时动态分配和释放(典型的有malloc()-free()及new-delete,后面会讲到)。
存储类
C与C++中有五大存储类别,每个类别对应相应的存储时期和作用域,也是对应上述作用域和存储时期的组合,不包括动态存储时期。
五大类分别是自动,寄存器,静态空连接,静态外部链接,静态内部链接。
存储类 | 存储时间 | 作用域 | 链接性 | 关键字 |
自动 | 自动 | 代码块 | 空链接 | auto或默认 |
寄存器 | 自动 | 代码块 | 空链接 | register |
静态空连接 | 静态 | 代码块 | 空链接 | static |
静态外部链接 | 静态 | 文件 | 外部 | extern |
静态内部链接 | 静态 | 文本 | 内部 | static |
下面对五大存储类做进一步说明:
自动存储类:自动存储类是用得最多的一个类别,就是我们平时在代码块中定义的局部变量,其具有auto关键字,可声明也不可声明,变量默认是自动类型的,加上auto可以让代码阅读更加清晰易懂;一般自动存储类变量声明在块中,也就是{}中,抑或在函数的头部()作为函数参数,其作用直至函数结束。其内存管理实现方式,及堆栈在后面再做详细介绍。
寄存器存储类:寄存器存储类变量存放于CPU中的寄存器,其目的是提高变量读取速度,对于一些时常用到的变量最好不过了,需要注意的一点是:寄存器变量没有地址,即对寄存器变量的一切地址操作都是非法的。
静态空链接:静态空链接变量是在代码块中的静态变量,只局限于代码块中其声明之后的位置使用,对于每次调用某个函数,若非修改其值不会变,尽管在声明时初始化,第二次调用时也不会再次初始化。
静态外部链接:静态外部链接变量一般是在所有代码块外面的文本上声明的,一般常见于文件头,其可用于包含声明文件的文件使用,必须加上extern关键字做引用声明。
静态内部链接:静态内部链接也声明也代码块外的文本上,其只能用于当前文件,必须用static关键字声明,必要时可以用extern做引用声明。
静态变量与自动变量之间的关联及注意点:
在代码块中交替使用静态与自动变量时难免产生名字冲突。在子代码块中声明与父代码块的自动变量相同的自动变量会覆盖父代码块的自动变量,直至代码块结束;如果在代码块中声明与静态内/外变量相同名称的自动变量时,自动变量将在代码块中覆盖静态变量,直至代码块结束才恢复静态变量的作用。
在代码块中可以选择性地对代码块外部的静态变量做引用声明。
自动变量可以用以初始化话的变量来初始化,而静态变量只能用常量初始化(包括sizeof())。
引用声明不能初始化变量。
与C不同,C++中代码块外变量声明默认是static的,若需要声明静态外部链接变量,需要在前面加上extern关键字。
下面对C++类中的static静态变量做特别说明:
类中的static静态变量编译时便在内存中开辟空间存放,其不依赖于对象,而是依赖于类,生成多个对象时,共用一个静态变量。
需要注意的是,类中的非静态成员函数不能访问或通过this指针访问静态变量。
至此五大存储类介绍完毕,下面再对其他的有关内存管理的关键字进行说明:
const:常量关键字,标示符一旦进行const声明,以后其值不能更改,即只能在声明时初始化。const 容易混淆的概念是常量指针和指针常量,const *是常量指针,顾名思义就是指向常量的指针,不能通过当前指针来修改其指向的内存空间的值;* const是指针常量,即一旦声明,指针所指向的地址空间便不能更改。
volatile:volatile关键字用于程序的效率优化,在程序运行过程难免有其他操作修改内存中的值,比如多线程同时执行时;volatile关键字声明变量不会受到其他操作的干扰,编译器知道当前变量的属性之后,会将其放于寄存器中,提高读取效率;反之,如果volatile声明之后有其他操作修改了变量在内存中的值,那么当前操作得到的在寄存器中的值就不是正确的。
restrict :restrict关键字指明当前变量有唯一的访问入口,这样做的好处是,如果对一个变量做多次运算,那么编译器会将其简化为一次运算,减少内存的读取次数。比如对一个变量做两次加法运算,第一次+1,第二次+2,那么编译器会简化为一次+3的运算。需要注意的是,如果在两次加法之间存在第二个访问入口修改变量的值,那么两次运算将得到错误的结果,所以restrict关键字的使用需要谨慎。
mutable :mutable有声明常量中的个别变量作用,如:
struct point{
mutable int x;
int y;
};
const point a;
那么使用常量a时,y不能修改,x可以修改。
注意:
const,volatile是C90出现的,restrict是C99出现的。