c++内存管理

C和C++内存模型

(1)C语言编程中的内存基本构成

C的内存基本上分为4部分:静态存储区、堆区、栈区以及常量区。他们的功能不同,对他们使用方式也就不同。

1.栈 ——由编译器自动分配释放;
2.堆 ——一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收;
3.全局区(静态区)——全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量
和未初始化的静态变量在相邻的另一块区域(C++中已经不再这样划分),程序结束释放;

4.另外还有一个专门放常量的地方,程序结束释放;
(a)函数体中定义的变量通常是在栈上;
(b)用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上;
©在所有函数体外定义的是全局量;
(d)加了static修饰符后不管在哪里都存放在全局区(静态区);
(e)在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用;
(f)在函数体内定义的static表示只在该函数体内有效;
(g)另外,函数中的"adgfdf"这样的字符串存放在常量区。

(2)C++编程中的内存基本构造

在C++中内存分成5个区,分别是堆、栈、全局/静态存储区、常量存储区和代码区;

1、栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区,里面的变量通常是局部变量、函数参数等。
2、堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如
果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
3、全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在
C++里面没有这个区分了,他们共同占用同一块内存区。
4、常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)。
5、代码区 (.text段),存放代码(如函数),不允许修改(类似常量存储区),但可以执行(不同于常量存储区)。

内存模型组成部分:自由存储区,动态区、静态区;

根据c/c++对象生命周期不同,c/c++的内存模型有三种不同的内存区域,即:自由存储区,动态区、静态区。
自由存储区:局部非静态变量的存储区域,即平常所说的栈;
动态区: 用new ,malloc分配的内存,即平常所说的堆;
静态区:全局变量,静态变量,字符串常量存在的位置;
注:代码虽然占内存,但不属于c/c++内存模型的一部分;

一个正在运行着的C编译程序占用的内存分为5个部分:代码区、初始化数据区、未初始化数据区、堆区 和栈区;
(1)代码区(text segment):代码区指令根据程序设计流程依次执行,对于顺序指令,则只会执行一次(每个进程),如果反复,则需要使用跳转指令,如果进行递归,则需要借助栈来实现。注意:代码区的指令中包括操作码和要操作的对象(或对象地址引用)。如果是立即数(即具体的数值,如5),将直接包含在代码中;
(2)全局初始化数据区/静态数据区(Data Segment):只初始化一次。
(3)未初始化数据区(BSS):在运行时改变其值。
(4)栈区(stack):由编译器自动分配释放,存放函数的参数值、局部变量的值等,其操作方式类似于数据结构中的栈。
(5)堆区(heap):用于动态内存分配。

更多见:C和C++内存模型

有了malloc/free为什么还要new/delete?

malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
  对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。

内存耗尽怎么办?

如果在申请动态内存时找不到足够大的内存块,malloc和new将返回NULL指针,宣告内存申请失败。

全局变量的初始化

什么时候初始化

根据 C++ 标准,全局变量的初始化要在 main 函数执行前完成,常识无疑,但是这个说法有点含糊,main 函数执行前到底具体是什么时候呢?是编译时还是运行时?答案是既有编译时,也可能会有运行时(seriously), 从语言的层面来说,全局变量的初始化可以划分为以下两个阶段(c++11 N3690 3.6.2):

static initialization: 静态初始化指的是用常量来对变量进行初始化,主要包括 zero initialization 和 const initialization,静态初始化在程序加载的过程中完成,对简单类型(内建类型,POD等)来说,从具体实现上看,zero initialization 的变量会被保存在 bss 段,const initialization 的变量则放在 data 段内,程序加载即可完成初始化,这和 c 语言里的全局变量初始化基本是一致的。

dynamic initialization:动态初始化主要是指需要经过函数调用才能完成的初始化,比如说:int a = foo(),或者是复杂类型(类)的初始化(需要调用构造函数)等。这些变量的初始化会在 main 函数执行前由运行时调用相应的代码从而得以进行(函数内的 static 变量除外)。

需要明确的是:静态初始化执行先于动态初始化! 只有当所有静态初始化执行完毕,动态初始化才会执行。显然,这样的设计是很直观的,能静态初始化的变量,它的初始值都是在编译时就能确定,因此可以直接 hard code 到生成的代码里,而动态初始化需要在运行时执行相应的动作才能进行,因此,静态初始化先于动态初始化是必然的。
初始化的顺序

对于出现在同一个编译单元内的全局变量来说,它们初始化的顺序与他们声明的顺序是一致的(销毁的顺序则反过来),而对于不同编译单元间的全局变量,c++ 标准并没有明确规定它们之间的初始化(销毁)顺序应该怎样,因此实现上完全由编译器自己决定,一个比较普遍的认识是:不同编译单元间的全局变量的初始化顺序是不固定的,哪怕对同一个编译器,同一份代码来说,任意两次编译的结果都有可能不一样[1]。

因此,一个很自然的问题就是,如果不同编译单元间的全局变量相互引用了怎么办?

当然,最好的解决方法是尽可能的避免这种情况(防治胜于治疗嘛),因为一般来说,如果出现了全局变量引用全局变量的窘况,那多半是程序本身的设计出了问题,此时最应该做的是回头重新思考和修改程序的结构与实现,而不是急着穷尽技巧来给错误的设计打补丁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值