C/C++中内存管理小结

来源:互联网 发布:日漫中的中国知乎 编辑:程序博客网 时间:2024/06/05 15:29

参考《高质量程序设计指南C++/C》

一、       内存分配方式:

3种:

(1)      从静态存储区分配。内存在程序编译的时候已经分配好了,在程序的整个运行期间都存在。如全局变量,static变量等。

(2)      在堆栈上分配。在函数执行期间,函数内部变量(包括形参)的存储单元都创建在堆栈上,函数结束时这些存储单元自动释放(堆栈清退)。效率高,但分配的内存容量有限,可能出现堆栈溢出。

(3)      从堆(heap)或自由存储空间上分配,也称动态内存分配。程序运行期间用malloc()或new申请内存,生存周期由程序员决定。

原则:如果使用堆栈存储和静态存储就能满足应用要求,那么就不要使用动态存储,因为在堆上动态分配内存需要额外开销。

 

二、       常见内存错误及其对策:

(1)      内存分配未成功,却使用了它。

解决办法是在使用内存之前检查指针是否为NULL。如果指针p是函数的参数,那么在函数的入口处使用assert( p!=NULL )进行检查以避免输入非法参数;如果是用malloc()或new申请内存,应该用if( p==NULL )、if( p!=NULL )或者捕获异常来进行错误处理。

 

(2)      内存分配虽然成功,但尚未初始化就使用它。

初始化数组、指针和动态内存,防止将未初始化的内存作为右值使用。

 

(3)      内存分配成功并且已经初始化,但操作越过了内存的边界。

注意数组或指针的下标不要越界,特别要当心发生“多1”或“少1”操作。

 

(4)      忘记了释放内存或者只释放了部分内存,因此造成内存泄漏。

动态申请与释放必须配对,程序中malloc()/new与free()/delete是用次数一定要相同。

 

(5)      释放了内存却还在继续使用它。

用free或delete释放了内存后,立即将指针值置为NULL,防止产生“野指针”。

 

三、       指针参数是如何传递内存的:

如果函数的参数是一个指针,不要指望用该指针去申请动态内存。如下例:

void getMemory(char *p, int num )

{

         p = (char*)malloc( sizeof(char) * num );

}

编译器总要为每个函数的每个参数制作临时副本,指针参数p的副本是_p,编译器使_p=p。如果函数体内的程序修改了_p指向的内容,就导致参数p指向的内容也做出了相应的修改。但在本例中,_p申请了新的内存,只是把_p本身的值改变了,即指向了新的内存空间,但p本身未变(即修改了_p本身的值,而不是_p指向的对象)。

         如果非得用指针参数去申请内存,应该用“指向指针的指针”或者“指针的引用”。

void getMemory2(char **p, int num )    //或者是char*&rp

{

         *p = (char*)malloc( sizeof(char) * num );

}

void Test2(void)

{

         char *str = NULL;

         getMemory2(&str, 100);          //注意是&str,而不是str

         strcpy(str, "hello");

         cout << str << endl;

         free( str );

}

 

可以用函数返回值来传递动态内存,这种方法更简单。

char  * getMemory3( int num )    

{

         char *p = (char*)malloc( sizeof(char) *num );

         return p;

}

void Test3(void)

{

         char *str = NULL;

         str = getMemory3(100)

         strcpy(str, "hello");

         cout << str << endl;

         free( str );

}

注意此处不要用return 语句返回指向“栈内存”的指针或引用。

 

四、       free和delete把指针怎么了?

free和delete只是把指针所指的内存释放掉,并没有把指针本身删掉。使用free和delete释放后的指针变成了“野指针”,应在释放后将指针置为NULL。

 

五、       动态内存会被自动释放吗?

函数体内的局部变量在函数结束时自动消亡,但它所指的动态内存不会自动释放。

void Func(void)

{

         char *p = (char*)malloc(100); //动态内存会被自动释放吗?

}

         这是由于指针变量和其所指向的内存空间不是同一回事。

(1)      指针消亡了,并不表示它所指向的内存会被自动释放;

(2)      内存被释放了,并不表示指针会消亡或成了NULL。

 

六、       杜绝野指针:

“野指针”不是NULL指针,而是指向“非法”内存的指针。

野指针的成因有3个:

(1)没有初始化指针变量。所以指针变量在创建的同时应该初始化,要么将指针设置为NULL,要么让它指向有效内存。

char *p = NULL;                                            //NULL为合法指针值

char *str = (char*)malloc(100);                 //指向有效内存区

 

(2)指针p被free或delete之后,没有置为NULL。

 

(3)指针操作超越了变量的作用范围。

 

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

malloc()和free()是C++/C语言的标准库函数,new和delete是C++的运算符。

对于非内部数据类型的对象而言,光用malloc()和free()无法完成动态对象的要求:对象在创建的同时要自动调用构造函数,对象在销毁的时候要自动调用析构函数。由于malloc()和free()是库函而不是运算符,不在编译器控制权限之内,不能把调用构造函数和析构函数的任务强加给它们。

malloc()和free()不能调用构造函数和析构函数完成初始化和清楚工作,所以不要企图用malloc()和free()来完成动态对象的内存管理,应该用new/delete。对于内部数据类型而言,没有构造与析构过程,malloc()和free()与new和delete是等价的。

为什么不把malloc()free()淘汰?

(1)      C程序只能用malloc()和free()管理动态内存;

(2)      使用new/delete更安全。new能自动计算他要构造的对象的字节数量,而malloc不能;new直接返回目标类型的指针,不需要显示地转换类型,而malloc返回void *,显示转换成目标类型后才能使用;

(3)      可以自定义重载new/delete;

(4)      在某些情况下,malloc()/free()提供比new/delete更高的效率。

 

八、       malloc/free的使用要点:

函数malloc()的原型如下:

void  *malloc(size_t size);

         注意力集中在两个要素上:“类型转换”和“sizeof”。

(1)            malloc()函数返回值的类型是void*,所以在调用malloc()时要显示地进行类型转换,将void *转换为需要的指针类型;

(2)            malloc()函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数。用sizeof确定要申请类型的字节数。

 

函数free()的原型如下:

void free( void * memblock);

如果p是NULL指针,那么函数free()对p无论操作多少次都不会出问题;如果p不是NULL指针,那么函数free()对p连续操作两次就会导致程序运行时出现错误。

 

九、       new/delete的使用要点:

new使用起来要比函数malloc()简单的多,这是因为new内置了sizeof、类型转换和类型安全检查功能。对于非内部数据类型的对象而言,new在创建动态对象的同时完成了初始化工作。如果对象有多个构造函数,new的语句也可以有多种形式。

class Obj

{

public:

         Obj();                           //默认构造函数

         Obj(int x);                   //带一个参数的构造函数

         …………

}

 

void Test(void)

{

         Obj *a = new Obj;

         Obj *b = new Obj(2012);  //初值为2012

         ……………………

         delete a;

         delete b;

}

如果用new创建对象数组,那么智能使用对象的默认构造函数,例如:

Obj *objects =new Obj[100];

        

         多次delete一个不等于NULL的指针会导致运行时错误,但多次delete一个NULL指针没有任何危险,因为delete的运算符会首先检查这种情况,如果指针为NULL,则直接返回。

 

十、       内存耗尽怎么办?

在申请动态内存时找不到足够大的连续字节内存块,malloc()和new会使用不同的方式宣告内存申请失败。

(1)      判断指针是否为NULL,如果是则立刻用return语句终止本函数;

voidFunc(void)

{

   A *a = new(nothrow)A;

   if( a==NULL )

            return;

   ………………

}

(2)      判断指针是否为NULL,如果是则立刻用exit(1)终止整个程序的运行;

voidFunc(void)

{

   A *a = new(nothrow)A;

   if( a==NULL )

            exit(1);

   ………………

}

(3)      为new和malloc()预设异常处理函数;

(4)      捕获new抛出的异常,并尝试从中恢复。

 

对于32位以上的应用程序而言,一般情况下使用malloc()和new几乎不可能导致“内存耗尽”,因为32位操作系统支持“虚拟内存”,内存用完了,自动用硬盘空间顶替。

 

 

 

还有一些知识点目前还没用到,暂时不能理解深刻,暂不做介绍,主要包括:

(1)      new的三种使用方式;

(2)      用对象模拟指针;

(3)      带有引用计数的智能指针;

(4)      智能指针作为容器元素;

原创粉丝点击