主要内容:动态内存malloc、calloc、realloc、free
一、 动态内存基本概念
- 动态内存定义
动态内存就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不像数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。在实际中当用户无法确定空间大小,或者空间太大,栈(一般大小1Mb)上无法分配时,会在堆上(一般为1.5G左右,可改变)采用动态内存分配。 - 动态内存与静态内存比较
静态内存是指在程序开始运行时由编译器分配的内存,它的分配是在程序开始编译时完成的,不占用CPU资源。程序中的各种变量,在编译时系统已经为其分配了所需的内存空间,当该变量在作用域内使用完毕时,系统会自动释放所占用的内存空间。变量的分配与释放,都无须程序员自行考虑。
区别:
a) 静态内存分配在编译时完成,不占用CPU资源; 动态内存分配在运行时,分配与释放都占用CPU资源。
b) 静态内存在栈(stack)上分配; 动态内存在堆(heap)上分配。
c) 动态内存分配需要指针和引用类型支持,静态不需要。
d) 静态内存分配是按计划分配,由编译器负责; 动态内存分配是按需分配,由程序员负责。 - 为什么要使用动态内存分配
以一个数组为例:在定义一个数组时给定了其内存空间,只要给定足够大的空间,就可以放入你所需的数据元素。但是定义数组时设置足够大的内存空间看似简单,但却有以下缺点:
a)如果内存空间很大,数据却很少就会使内存空间浪费。
b)如果内存空间不足就会出现溢出的现象。
二、 malloc
问题引入:在筛选法求素数问题中,我们想要定义一个长度为n的数组来保存n个元素,我们想法如下:
int arr[n]; //但这种定义是错误的,不过C99支持
为了达到上述目的,我们引入malloc来开辟动态数组。它的函数原型如下:
//头文件
#include<malloc.h>
//函数原型
void *malloc(sizt_t size); //参数为所需要分配的字节数
它的一般形式为:
malloc需要主要的几点:
a)在C语言中不是关键字而是C函数库中提供的函数。
b)在调用malloc时,系统会在内存池(堆)中提取一块内存连续的空间,并向该程序返回一个这块内存的指针。
c)在有的编译器下申请的空间略大于你请求申请的内存空间。
d)malloc进行内存分配时会返回void*类型的指针,但是这个指针可以转化为任意类型的指针。
例1:用malloc创建10个长度的数组,并将每个格子置为0
int main
{
int n = 10;
int *p = (int *)malloc(n*sizeof(int));
int i;
for(i=0;i<n;i++)
{
p[i] = 0; //或者*(p+i) = 0;
free(p);
return 0;
}
}
三、 calloc
它的函数原型如下:
//有文件
#include<stdlib.h>
//源文件
void* calloc(size_t num_elements,size_t element_size);
特点:calloc在函数原型中比malloc多了一个参数,它将malloc的字节长分为所需元素的个数和每个元素的字节数两个参数传进去。但是calloc的最大特点是将所有元素初始化为0。
缺点:因为它只能将元素置为0,不能置为其他,所以局限性很大,一般不常用。同时参数是数字,容易写反,但结果确定不变,误导性强。
例2:用calloc创建10个长度的数组,并将每个格子置为0
int main
{
/*
int n = 10;
int *p = (int *)malloc(n*sizeof(int));
int i;
for(i=0;i<n;i++)
{
p[i] = 0; //或者*(p+i) = 0;
*/
int *p = (int *)calloc(n,sizeof(int)); //块注释里面的内容即是calloc的功能实现,
//可以使用这一句替换
free(p);
return 0;
}
}
四、 realloc
它的函数原型如下:
//头文件
#include<stdlib.h>
//原型
void realloc(void *ptr, size_t new_size);
realloc函数用于修改一个原先已经分配的内存块的大小。使用realloc 一般步骤有四个:(以买新房子为例)
a)重新开辟一个动态内存(买新房)
b)将旧的数据移入(搬入新房)
c)删除旧的动态内存(卖掉旧房)
d)更新地址(告诉其他人新房地址)
例3:重新开辟一个动态内存、将旧的移入,删除旧的,并且更新地址
int main()
{
int n = 10;
int *p = (int *)malloc(n*sizeof(int));
int i;
for(i=0;i<n;i++)//模拟数组被使用
{
p[i] = i;
}
/*
int *q = (int *)malloc(n*2*sizeof(int));//1、买新房
for(i=0;i<n;i++)//2、搬家
{
q[i] = p[i];
}
free(p); //3、卖旧房
p = q; //4、更新地址
q = NULL;
*/
p = (int *)realloc(p,n/2*sizeof(int)); //块注释里面的内容即是realloc的功能实现,
//可以使用这一句替换
return 0;
}
五、free
函数原型:
free(void* pointer);
1. 在申请动态内存后,一定要记得释放。否则会发生内存泄漏(开辟新内存后没有释放)。
2.内存回收的两种方式:
a)进程结束
b)关机
3.free容易引起程序(进程)崩溃的原因:
a)数组越界。多写等号、内存开辟大小sizeof()忘记写、realloc第二个参数错误。
int *p = (int *)malloc(n*sizeof(int));
int i;
for(i=0;i<n;i++) //这段代码中,如果多写“=”,越界,破坏了内存尾部。
{
p[i] = 0;
}
b)破坏头部信息
int *p2 = (int *)malloc(20*sizeof(int));
/*for(i=0;i<20;i++)
{
*p2 = 0;//将动态内存置0,破坏头部信息,再做p2++时,指针不知道移到哪去了。
p2++;
}*/
c)重复释放同一内存
double *p3 = (double *)malloc(10*sizeof(double));
double *p4 = p3;
free(p3);
free(p4); //崩溃,已经释放过,不能二次释放
d)释放不是动态创建的内存
int arr[10];
free(arr);//崩溃
free(NULL); //正确