最近在看林锐《高质量C++编程指南》,第18章中讲到内存管理一章,做个总结。
1.内存分配的三种方式:
a.静态存储区域分配.内存在程序编译的时候就已经分配好了,已经编址,而且程序整个运行过程中一直存在。如全局变量,static静态变量等。
b.栈上创建. 自动创建,自动释放,效率高,但是分配的容量有限,作用域为函数执行期间,函数执行完后自动释放。如局部变量,函数参数等。
c.堆上创建. 手动申请,手动释放。使用灵活,但容易引起额外开销。如出现内存碎片,动态分配失败,对象的销毁问题。
2.申请内存中易出现的问题,现从常见的例子解释
void GetMemory(char *p, int num)
{
*p = (char*)malloc(num);
}
int main(void)
{
char*str = NULL;
int len =0;
GetMemory(str, 100);
strcpy(str, "hello");
puts(str);
free(str);
return 0;
}
上例中,GetMemory()中p是栈内存,随着函数的结束内存也被系统回收,传入的参数只是p的初始化的值,和str没任何关系。所以strcpy拷贝时会出现内存泄露。
若将Gememory()函数改为:
char* GetMemory(int num)
{
char p[] = "Hello world";
return p;
}
由于p是栈内存的指针,程序结束时内存释放了,就会造成内存泄露。(main()中内容需要对应修改)
若将Gememory()函数改为:
char* GetMemory( int num)
{
char *p = (char*)malloc(num);
return p;
}
此时p所指向的内存为堆上的内存,返回的是堆上的内存指针,没有销毁。所以此时没有造成内存泄露。(main()中内容需要对应修改)
或者我们也可以把它给我穿指针或者引用来达到目的:
void GetMemory(char **p, int num)
{
*p=(char*)malloc(num);
}
再修改main()函数,也能达到传递内存的要求。
若将GetMemory()改写为:
char* GetMemory( int num)
{
char *p = “hello world”;
return p;
}
此时p所指向的为常量字符串的内存,分配在静态区。在程序生命期内始终有效。但此时p实际上是const char*p,它指向的内存区域是个常量,所以不能修改,如果你想通过strcpy给它赋值的话就会出错(常量值不可修改)。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void main()
{
char *sr=(char*)malloc(100);
strcpy(sr,"hello");
free(sr);
if(sr!=NULL)
strcpy(sr,"world");
printf("%s",sr);
}
输出为:world,虽然有结果输出,但是却是错误的,危险的。
free()是对所申请的内存块的空间进行释放,由操作系统回收,如果没有delete sr操作的话,sr仍指向之前所申请的内存块的指针,并不为NULL,但这个内存块可能已经被其他的程序应用,此时再修改这个内存块的内容就会出现未知的错误,是危险的操作。
3.new / delete和malloc / free
c++中引入了new和delete。是因为new和delete会隐式的调用类的构造函数和析构函数,对面向对象的c++很方便,如果用malloc / free则要显示的调用构造函数和析构函数。malloc / free适用于c中,对于普通类型的数据,两者通用。而且,对于普通类型,delete p和delete[] p的效果一样。
由于malloc的函数原型为void *malloc( size_t size );所以我们在申请时要就它进行强制转换,如int *p=(int*)malloc(sizeof(int)*10);所以在判断是否申请成功时可以用判断语句if(p==Null){return;}来判断申请的内存是否成功,虽然说对于32位以上的机子只有很小的概率会出现不成功的情况,但为了程序的健壮性还是要加上。销毁申请的内存直接用free(p);p=Null。释放了后一定要置空,否则就会出现野指针。
对于new的原型为:
::opt new placementoptnew-type-name new-initializeropt
::opt new placementopt( type-name ) new-initializeropt
new的使用分为3种,plain new,nothrow new,placement new。
第一种是通过抛出异常机制来判断是否申请内存成功,所以我们要通过try...catch...来捕获异常。使用方法如:try{int *p=new int[100];...delete p;}catch(std::bad_alloc&ex){cer<<ex.what()<<endl;}
第二种是通过返回void*来判断是否成功,所以我们可以和malloc一样来判断是否申请成功。使用方法如:int *p=new(nothrow) int[100];if(p==Null),return;
第三种是允许在一块已经分配成功的内存上重新构造对象或对象数组,不使用,不作解释。