CC++动态分配内存的⼏种⽅法
使⽤C/C++编程时,会经常动态分配内存,以便合理使⽤内存,本⽂主要讲述动态内存分配的⼏种⽅法及⼀些原理,理解不深刻之处欢迎指教。
引⾔
为什么要进⾏动态内存分配?以数组为例,数组元素在内存中存储的地址是连续的。声明⼀个数组后,该数组需要的内存⼤⼩在程序编译时就被分配,但是该数组实际所需内存⼤⼩在程序运⾏时才知道,若运⾏时发现数组所需内存⼤于编译时的分配好的数组内存,就会报错,⽰例程序如下:
#include<stdio.h>
int main(void)
{
int arr[3];
for(int i =0; i <5; i++)
scanf("%d",&arr[i]);
for(int i =0; i <5; i++)
printf("%d ", arr[i]);
printf("\n");
return0;
}
⾸先声明了有三个元素的数组arr,但是在实际输⼊时输⼊了5个元素,也就是说编译器在编译时给arr分配了3个int型⼤⼩的内存块,但是在运⾏时需要5个int型⼤⼩的内存块,就会报如下错误。
因此⼈们采取了⼀个简单的⽅法,就是声明⼀个⾜够⼤的数组,例如int arr[1000]。但是该⽅法有以下缺点:
(1)若程序所需的数组元素个数⼤于1000,也会造成上述错误;
(2)若程序所需的数组元素个数只有5,会浪费很⼤的内存空间;
(3)若程序所需的数据元素个数⼤于1000,为了提⾼程序的鲁棒性,应该做出合理的响应,⽽不是简单报错。
为了解决上述问题,就需要动态分配内存,所谓动态分配内存,通俗理解是需要多⼤的内存,就分配多⼤的内存。在C语⾔中,与动态内存分配有关的函数为malloc、calloc、realloc、free,这些函数在库⽂件<stdlib.h>中声明;C++中,与动态内存分配有关的运算符为new、delete。
malloc
malloc是C语⾔提供的执⾏内存分配的函数。malloc函数原型为"void *malloc(size_t size);",当程序调⽤malloc函数时,malloc就从内存池中提取相应⼤⼩的连续内存块,并返回指向该内存块起始位置的指针。⽰例程序如下:
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int*p;
int count;
scanf("%d",&count);//输⼊元素个数
p =(int*)malloc(count*sizeof(int));//分配内存空间,将⾸地址赋给p
if(p ==NULL)//若分配失败,报错
exit(-2);
for(int i =0; i < count; i++)
scanf("%d", p + i);
for(int i =0; i < count; i++)
printf("%d ",*(p + i));
printf("\n");
free(p);
return0;
}
关于malloc函数,注意以下⼏点:
(1)malloc申请的内存是连续的,由于数组的内存也是连续的,因此⼆者有相通之处,*(p+i)可以表⽰为数组的第(i+1)个元素;
(2)如果内存池是空的,malloc会向操作系统请求得到更多的内存,然后进⾏分配;如果操作系统⽆法向malloc提供更多的内存,malloc 就会返回NULL指针,NULL其实就是⼀个宏定义,值为0,因此对p的值检查是⼗分必要的,如果p为NULL,表⽰没分配到内存,⾃然就不能进⾏接下来的步骤了。
(3)观察函数原型"void *malloc(size_t size);"可知,malloc返回的是void *类型的指针,这是因为malloc⽆法得知程序⽤分配好的内存块存储什么类型的数据(例如int、char、double等),因此返回void *类型,⽽void *类型可以转为其他任何类型的指针,上述程序就将void *类型强制转换为int *类型,以便该内存块存储int型的数据。
(4)从指针⾓度看,因为指针存储的是变量的地址,是⼀个地址值,因此“p = (int )malloc(count sizeof(int))”这个操作可以理解为对指针p初始化。
calloc
calloc也⽤于内存分配,函数原型为"void *calloc(size_t num_elements,size_t element_size);",⽰例程序如下:
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int*p;
int count;
scanf("%d",&count);
p =(int*)calloc(count,sizeof(int));
if(p ==NULL)
exit(-2);
for(int i =0; i < count; i++)
printf("%d ",*(p + i));
printf("\n");
free(p);
return0;
}
程序运⾏结果如下:
该函数和malloc有以下区别:
(1)申请完内存块后,calloc将该内存块存储的值初始化为0,然后才返回指向该内存块起始位置的指针;
(2)两个函数请求内存⼤⼩的⽅式不同,可见参数列表。
realloc
realloc主要⽤于修改已经分配的内存的⼤⼩,函数原型为“void *realloc(void *ptr,size_t new_size);”,ptr代表已经分配内存的⾸地址,new_size代表修改后的⼤⼩。若⽤于扩⼤⼀个内存块,该内存块原先的内容保留,新增加的内存添加到原先的内存块的后⾯,并且新增加的部分没有初始化;若⽤于缩⼩⼀个内存块,该内存块的尾部的部分内存被裁减掉,前⾯的部分内存的内容依然保留。⽰例程序如下:
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int*p,*newbase;
int count;
scanf("%d",&count);
p =(int*)malloc(count*sizeof(int));
if(p ==NULL)
exit(-2);
for(int i =0; i < count; i++)
scanf("%d", p + i);
for(int i =0; i < count; i++)
printf("%d ",*(p + i));
printf("\n");
newbase =(int*)realloc(p,2*count*sizeof(int));
if(newbase ==NULL)
exit(-2);
for(int i = count; i <2* count; i++)
scanf("%d", newbase + i);
for(int i =0; i <2*count; i++)
printf("%d ",*(newbase + i));
printf("\n");
free(newbase);
return0;
}
程序运⾏结果如下:
sizeof 指针 关于该函数,注意:若原先的内存块的⼤⼩⽆法改变,realloc将分配另⼀块指定⼤⼩的内存块,并将原先内存块的内容复制到新的内存块上。若发⽣这种情况,内存块的⾸地址就发⽣了改变,因此应使⽤新的指针接收realloc函数的返回值。
free
当动态分配的内存不再需要时,应该将其释放掉,以便以后重新分配,若使⽤完不释放会引起内存泄漏。free函数⽤来释放malloc、calloc、realloc申请的内存空间,函数原型为“void free(void *ptr)”,ptr是指向要释放的内存块的指针,该函数没有返回值,free的参数若是NULL,则free不会产⽣任何效果。⽰例程序如下:
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int*p;
p =(int*)calloc(5,sizeof(int));
if(p ==NULL)
exit(-2);
printf("地址和值分别为:%p %d\n", p,*p);
for(int i =0; i <5; i++)
printf("%d ",*(p + i));
printf("\n");
free(p);
printf("释放后地址和值分别为:%p %d\n", p,*p);
p =NULL;
printf("地址为:%p\n", p);
return0;
}
运⾏结果如下:
注意:使⽤free释放的是内存块,⽽不是ptr这个指针变量,ptr的这个指针变量仍然指向该内存空间,只不过该内存空间的内容是毫⽆意义的是,是未定义的,为了防⽌以后程序不⼩⼼使⽤了它,要把它指向NULL。
new
new是C++的⼀个关键字,也是操作符,C语⾔并没有new,new也经常被⽤于动态内存分配,⽰例程
序如下:
#include<iostream>
using namespace std;
int main(void)
{
int*p,*q,*s;
p =new int;//申请分配⼀个存储int型数据的内存块,相当于"p=(int *)malloc(sizeof(int));"
q =new int(0);//申请分配⼀个存储int型数据的内存块,并初始化为0,相当于"q=(int *)calloc(sizeof(int))";
s =new int[10];//申请分配⼗个存储int类型数据的内存块,相当于"s=(int *)malloc(10*sizeof(int))";
delete p;
delete q;
delete[] s;
return0;
}
它和malloc有如下区别:
(1)new申请内存分配时⽆需指定内存块的⼤⼩,编译器会⾃⼰计算,malloc则需要使⽤sizeof等函数计算好内存块的⼤⼩;
(2)若申请成功,new会返回指定类型的指针,不需再进⾏类型转换;⽽malloc返回的是void *类型的,还需强制转换成我们需要的类型;
(3)若申请失败,new会抛出bac_alloc异常,⽽malloc会返回NULL。
关于更详细的解释,可见博客,这篇博客从更深层次的⾓度分析了malloc和new的区别。
delete
new申请的内存块不被释放,也会造成内存泄露,C++主要使⽤delete来释放new申请的内存。delete
⽤于释放new申请的单个元素分配的内存,delete[]⽤于释放new []申请的多个元素分配的内存,具体使⽤⽅法见上述程序。注意:使⽤delete释放的是内存块,⽽不是s这个指针变量,释放完后,s就变为随机值了,如果在接下来的程序中,没有给s赋新值就再调⽤s,由于s在内存中是随机值,就有可能指向重要地址,以致使系统崩溃,稳妥起见,如果delete之后程序还没结束,就将s的值赋为NULL。
补充
内存的分配⽅式有以下四种:
(1)从静态存储区域分配,例如全局变量,这些内存在程序编译时就已分配,程序运⾏期间都存在;
(2)从栈上分配,例如局部变量,这些内存在函数结束时被⾃动释放;
(3)从堆上分配,例如malloc函数申请的内存,动态内存的⽣存期不会随着函数结束释放,由程序员决定;
(4)其他,例如常量、⼆进制代码等,这些内存在程序运⾏期间⼀直存在,程序结束后释放。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论