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) 智能指针作为容器元素;
- C/C++中内存管理小结
- 【Object-C】内存管理小结
- objective-c 内存管理小结
- objective-c中内存小结
- Objectiv-C学习笔记-内存管理小结
- Object-C中管理内存
- C/C++中内存管理
- c语言中内存管理
- C中内存分配及管理小结 中英文对照 (Summary of C memory allocation and management Chinese&Englise)
- C语言中inet_ntoa()的内存管理
- C/C++中程序内存分配管理
- C语言中内存的管理
- C/C++中内存管理相关知识
- Obj-C中内存的管理一瞥
- Ojbect C中内存管理漫谈.
- 【C/C++】C++内存管理
- C/C++ 内存管理
- C内存管理函数
- ABCD……组成菱形
- Classic Shell Scripting
- Python 复制文件的方法
- 关于公司的SVN服务器的一些小事
- android学习之-activity的启动模式
- C/C++中内存管理小结
- 单元测试的效益
- 读取Execl数据到Datatable或DataSet时 日期单元格出现乱码的解决方案
- Android开发效率—Eclipse快捷键
- asp.net mvc Account/Login the rource can not be found
- 防止重复提交的各种用法 分享
- 详解 mysql explain
- 使用wget下载目录
- android gridview左右滑动 并能定位