一.C语言中内存管理:
我们学过的掌握申请内存,有申请数组的连续空间和内置类型的空间,但这两个都有局限性,不能申请动态内存管理,所以我们还要继续学习如何申请动态内存管理。
(一).malloc函数:
C语言中提供了一个函数malloc可以动态申请连续空间。
这个函数可以申请一块连续的空间,并且返回类型是指针。如果开辟成功则返回申请成功的地址指针,若开辟失败则返回NULL,所以我们在malloc申请完空间后一定要进行检测是否申请成功,如果size为0,则未定义取决于编译器。
C语言还提供了free函数,用来释放空间,以防内存泄漏。
如果ptr不是动态空间,则此时free'行为是未定义的;若ptr是NULL,不用进行。
int main()
{
int*p=((int*)malloc(sizeof(int)*10));
if(p=NULL)
{
perror("malloc");
return 1;
}
//此时p的动态空间已经申请成功,用free函数释放空间
//但是要用p置为NULL,因为 这里free函数相当于传值,形参是实参的拷贝
free(p);
p=NULL;
return 0;
(二).calloc函数:
C语言中还提供了一种calloc函数,和malloc函数功能基本相似都是申请动态空间,但是不同的是calloc函数在申请动态空间成功后,会对其进行初始化为0.
其中num表示有num个大小为size的申请空间。
函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。
int main()
{
int*b=(int*)calloc(10,sizeof(int);
if(P!=NULL)
{
}
int i=0;
for(int i=0;i<10;i++)
{
*(p+i)=i;
}
for(int i=0;i<10;i++)
{
printf("%d",*(p+i));
}
free(p);
p=NULL;
(三).realloc函数:
realloc函数的出现让动态内存管理更加灵活。 有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时 候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。
其中ptr 是要调整的内存地址 size 调整之后新大小 返回值为调整之后的内存起始位置。 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到 新 的空间。
注意:realloc在调整空间时候一般会遇到两种情况:(1).原有空间后面有足够大的空间,此时我们可以直接在原有空间的后面继续进行申请。(2).原有空间后面不足,此时我们需要扩展,在堆上重新找一个大小合适的空间进行申请,并且将旧的复制到新的空间中,释放掉旧的,这样函数就会返回一个新的地址。
(四).常见的动态内存错误:
(1).对NULL解引用错误使用
(2).对动态开辟空间的越界访问
(3).用free函数释放同一块空间
(4).用free函数释放动态空间的一部分,这是错误的,在释放的时候我们必须从空间的最开始进行释放。
(5).动态空间忘记释放,造成内存泄漏。
二.C++语言中内存管理:
(一).new/delete操作内置类型
在进行内存管理的讲解之前先看一下下面的题;
int globalVar = 1; static int staticGlobalVar = 1; void Test() { static int staticVar = 1;
int localVar = 1; int num1[10] = { 1, 2, 3, 4 }; char char2[] = "abcd"; const char* pChar3 = "abcd"; int* ptr1 = (int*)malloc(sizeof(int) * 4); int* ptr2 = (int*)calloc(4, sizeof(int)); int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4); free(ptr1); free(ptr3); }
1. 选择题: 选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区) globalVar在哪里?____ staticGlobalVar在哪里?____ staticVar在哪里?____ localVar在哪里?____ num1 在哪里?____ char2在哪里?____ *char2在哪里?___ pChar3在哪里?____ *pChar3在哪里?____ ptr1在哪里?____ *ptr1在哪里?____
1.动态申请一个内置类型的空间:
//动态申请一个int类型的空间
int*a=new int;
//动态申请一个int类型的空间并且初始化
int *b=new int(3);//初始化为3
//动态申请多个int类型的空间
int *c=new int[10];
//动态申请多个int类型的空间并且 初始化
int *d=new int[10]{0,1,2,3,4}//前五个值初始化为给定的值,剩余五个值默认为0
//动态清理空间
delete a;
delede []c;//注意:多个值的时候要在delete后面加[],内置定义类型可以不加,但是自定义类型必须加
注意:申请和释放单个元素的空间时候,用new和delete,若是申请和释放多个元素的空间时候,用new[]和delete[]结合使用。
2.动态申请自定义类型的 空间;
class A
{
public:
A(int a=0)
{
:_a(a);
{
cout<<"A():"<<this<<endl;
}
}
~A(int a=0)
{
cout<<"~A():"<<this<<endl;
}
private:
{
int _a;
}
int main()
{
A*p1=(A*)malloc(sizeof(A));
free(p1);
A*p2=new A(1);
delete P2;
若使用malloc给自定义对象申请空间 ,则只会开辟空间,但是用new申请空间,则会调用构造函数先初始化,然后申请空间,同理若使用delete,则先调用析构函数,然后释放掉空间。
类中有多个对象可以调用多次构造函数,然后每个函数调用一次析构函数。
int*p3=(int*)malloc(sizeof(int));
free(p3);
int *p4=new int(5);
delete p4;
但是如果是处理内置类型,使用malloc和new,free和delete效果一样。 注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会。
3.operator new和operator delete函数:
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是 系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过operator delete全局函数释放空间。operator new 实际也是通过malloc来申请空间,如果 malloc申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该措施 就继续申请,否则就抛异常。operator delete 最终是通过free来释放空间的。
4.new和delete的实现原理:
(1).内置类型;如果申请的是内置类型,则malloc、free、new和delete基本效果一样,new/delete申请或者销毁的是单个元素的空间,new[]和delete[]申请或者销毁的是多个元素的空间,如果申请失败,malloc则会返回NULL,new抛出异常。
(2).自定义类型:
new的原理:先用operator new函数申请空间然后调用构造函数在申请的空间上进行初始化。
delete的原理:先调用析构函数完成对象中的资源清理,再使用operator delete函数释放对象的空间。
new Q[N]:先用operator new[] 函数对N个对象申请空间,再调用构造函数在申请的空间上对N个值进行初始化。
delete Q[N]:先调用析构函数完成N个对象中的资源清理,再使用operator delete函数释放N个对象的空间。
5.定位new表达式:
概念:定位new表达式是在已分配的原始内存空间中调用构造函数初始化一个对象。 使用格式: new (place_address) type或者new (place_address) type(initializer-list) place_address必须是一个指针,initializer-list是类型的初始化列表 。如 果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。
class A
{
public:
A()
{
:_a(a);
{
cout<<"A():"<<this<<endl;
}
}
~A()
{
cout<<"~A():"<<this<<endl;
}
}
int main()
{
A*p1=(A*)malloc(sizeof(A));
//使用new调用构造函数初始化
new(p1)A;
//调用构造函数清理对象中的资源
p1->~A();
free(p1);
A*p2=(A*)malloc(sizeof(A));
new(p2)A;
p2->~A();
//使用operator delete释放空间
operator delete(p2);
return 0;
}
定位new的表达式:new(对象)类型。
6. malloc和new的区别:
malloc/free和new/delete的区别 :
malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。
不同的地 方是: 1. malloc和free是函数,new和delete是操作符
2. malloc申请的空间不会初始化,new可以初始化
3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可, 如果是多个对象,[]中指定对象个数即可
4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需 要捕获异常
6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new 在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成 空间中资源的清理 .