文档章节

C++内存管理

清风伴月
 清风伴月
发布于 2017/05/07 11:45
字数 1890
阅读 2
收藏 0

C++内存管理

一、内存分配方式

C++,内存分成5个区,分别是自由存储区全局/静态区常量存储区.

:存放函数参数以及局部变量,在出作用域时,将自动被释放.栈内存分配运算内置于处理器的指令集中,效率,但分配的内存容量有限.

:new分配的内存块(包括数组,类实例等),需delete手动释放.如果未释放,在整个程序结束后,OS会帮你回收掉.

自由存储区:malloc分配的内存块,需free手动释放.它和堆有些相似.

全局/静态区:全局变量(global)和静态变量(static)存于此处.(在以前的C语言中,全局变量又分为初始化的和未初始化的,C++不分)

常量存储区:常量(const)存于此处,此存储区不可修改.

1、堆与栈的区别

void f()

{

       int *p = new int[5];

}

上面一段代码就包含了堆与栈.指针P被分配在了栈中,而new出来的东西则被分配在了堆中,此句可以解释为”在栈中存放了一个指向堆内存的指针p”.(可否理解为:指针p的值是堆内存块的首地址??????)

主要区别:

管理方式不同: 栈是编译器自动管理的,堆需手动释放

空间大小不同: 在32位OS下,堆内存可达到4GB的的空间,而栈就小得可怜.(VC6中,栈默认大小是1M,当然,你可以修改它)

能否产生碎片不同:对于栈来说,进栈/出栈都有着严格的顺序(先进后出),不会产生碎片;而堆频繁的new/delete,会造成内存空间的不连续,容易产生碎片.

生长方向不同:栈向下生长,以降序分配内存地址;堆向上生长,以升序分配内在地址.

分配方式不同:堆动态分配,静态分配;栈分为静态分配和动态分配,比如局部变量的分配,就是动态分配(alloca函数),函数参数的分配就是动态分配(我想的).

分配效率不同:栈是系统提供的数据结构,计算机会在底层对栈提供支持,进栈/出栈都有专门的指令,这就决定了栈的效率比较高.堆则不然,它由C/C++函数库提供,机制复杂,堆的效率要比栈低得多.

可以看出,栈的效率要比堆高很多,所以,推荐大家尽量用栈.不过,虽然栈有如此多的好处,但远没有堆使用灵活.

二、控制C++的内存分配

其实C++的内存管理容易而且安全,因为当一个对象消除时,它的析构函数能够安全释放所有分配的内存.在嵌入式系统中,内存的分配是一个常见问题,保守的使用内存分配是嵌入式环境中的第一原则.

当你需使用new/delete时,一个防止堆破碎的通用方法是从不同固定大小的内存池中分配不同类型的对象(??????).对每个类重载new和delete就提供了这样的控制.

class TestClass

{

       void *operator new(size_t size);

       void operator delete(void *p);

};

void *TestClass::operator new(size_t size)

{

       void *p = malloc(size);

       return p;

}

void TestClass::operator delete(void *p)

{

       free(p);

}

对象数组的分配又不同于单个对象的分配,所以你仍需再重载new[]和delete[]操作符.但值得注意的是,对于C++而言,分配对象数组的大小等于数组参数的大小再加上额外的对象数目的一些字节,所以要尽量避免使用对象数组。

class TestClass

{

       void *operator new[](size_t size);

       void operator delete[](void *p);

};

void *TestClass::operator new[](size_t size)

{

       void *p = malloc(size);

       return p;

}

void TestClass::operator delete[](void *p)

{

       free(p);

}

void main()

{

       TestClass *p = new TestClass[10];

       delete[] p;

}

三、常见的内存错误及对策

1、内存分配未成功,却使用了它

解决办法:在使用之前检查指针是否为NULL,如果指针p是函数参数,那么在函数入口处assert(p!=NULL).如果是用malloc或new申请的话,应该用if(p==NULL)进行防错处理.

2、内存分配成功,但未初始化就使用它

解决办法:不要嫌麻烦,记得初始化就行了.

3、内存分配成功且已初始化,但操作越过了边界

解决办法:此问题通常出现于循环之中,注意不要多1或少1就行.

4、忘记释放内存

解决办法:含有这个错误的函数每调用一次就丢失一块内存,造成内存耗尽.记得free或delete就行.

5、 释放了内存却继续使用它

有三种情况:
      ※程序中对象的关系过于复杂,难以搞清哪个对象是否已经释放了内存.

      ※函数中return写错,返回了指向栈中的指针或引用.

      ※ free或delete后,没有将指针设为NULL,产生”野指针”.

四、指针与数组

C++中,指针和数组有着不少相似的地方,容易让我产生错觉,以为它们是等价的,其实不然。

数组在静态存储区或是栈上被创建,数组名对应着(而不是指向)一块内存,其地址与容量在生命周期内保持

不变。而指针可以随时指向任意类型的内存块,远比数组灵活,但也危险。
       

 char a[] = "hello";

        a[0] = 'x';

        char *p = "world";

        p[0] = 'y'; //试图修改常量字符串,编译器不能发现,执行会报错

杜绝野指针

“野指针”不是NULL指针,是指向”垃圾内存”的指针.它的缺省值是随机的,所以它会乱指一气.

产生”野指针”的原因有3种:

1、指针变量没有被初始化;

2、指针被free/delete后被没有设置为NULL;

3、指针操作超越了变量的作用域范围.如下例,p->fun()时,a已经消失。

  class A

       {

       public:

       void fun()

       {}

       };

       void Test()

       {

               A *p;

              {

                     A a;

              p = &a; //a的生命周期会在出作用域时结束

       }

       p->fun(); //p此时是"野指针"

}

五、malloc/free new/delete

有了malloc/free为何还需要new/delete呢? malloc/free是标准库函数,而new/delete是运算符,它们都可用于申请/释放动态内存.但对于非基本数据类型(比如类对象)而言, malloc/free无法自动执行对象的构造/析构函数.而new/delete却可以.

 malloc

函数malloc的原型: void *malloc(size_t size);

              函数malloc的使用:

              int *p = (int*)malloc(sizeof(int)*length);//length前是乘号

              可见,在使用malloc时需要进行类型转换.而使用sizeof运算符也是良好的代码风格.

 new

             new内置了sizeof,所以用起来写法更简洁。

             注意,使用new创建对象数组时,只能使用对象的无参数构造函数。

             如 Obj *o = new Obj[100];

六、内存耗尽怎么办?

解决办法:

1、判断指针是否为NULL,如果是立即返回。

void fun()

{

    A *a = new A();

    if(a==NULL)

           return;

}

2、判断指针是否为NULL,如果是立即终止。

void fun()

{

    A *a = new A();

    if(a==NULL)

           exit(1);

}

提示:不要不忍心使用exit(1),否则会害死OS.所以推荐使用方法2。不过搞笑的是,在32位OS上,永远也不会内存耗尽

© 著作权归作者所有

共有 人打赏支持
上一篇: Apache Hadoop
清风伴月
粉丝 1
博文 129
码字总数 255659
作品 0
海淀
程序员
私信 提问

暂无文章

2019 年最好的 7 款虚拟私人网络服务

糟糕的数据安全会带来极大的代价,特别是对企业而言。它会大致大规模的破坏并影响你的品牌声誉。尽管有些企业可以艰难地收拾残局,但仍有一些企业无法从事故中完全恢复。不过现在,你很幸运地...

linuxCool
39分钟前
1
0
OSChina 周一乱弹 —— 加油,还有11个小时就下班了

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @_全村的希望 :吴亦凡把大碗面正儿八经做成单曲了,你别说,还挺好听 《大碗宽面》- 吴亦凡 手机党少年们想听歌,请使劲儿戳(这里) @tom_t...

小小编辑
今天
295
13
C++ vector和list的区别

1.vector数据结构 vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。 因此能高效的进行随机存取,时间复杂度为o(1); 但因为内存空间是连续的,所以在进行插入和删除操作时,会造...

shzwork
今天
7
0
Spring之invokeBeanFactoryPostProcessors详解

Spring的refresh的invokeBeanFactoryPostProcessors,就是调用所有注册的、原始的BeanFactoryPostProcessor。 相关源码 public static void invokeBeanFactoryPostProcessors(Configu......

cregu
昨天
6
0
ibmcom/db2express-c_docker官方使用文档

(DEPRECIATED) Please check DB2 Developer-C Edition for the replacement. What is IBM DB2 Express-C ? ``IBM DB2 Express-C``` is the no-charge community edition of DB2 server, a si......

BG2KNT
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部