1.1 为什么要有动态内存分配呢?
我们已经掌握的内存开辟有两种方式:
int val=10;
char ch[5];
对于方式1,我们开辟的是一个整形大小(4个字节)的大小。对于方式2,我们开辟了一个连续的空间(5个字节)。在这里我们开辟的空间是固定的,我们无法在后续改变内存空间的大小,显得特别的不方便,所以我们要引入动态内存开辟来使得内存分配更为灵活。
2.1 malloc和free
2.1.1 malloc函数
void* malloc ( size_t size )
1.对于malloc函数,使用malloc函数向内存申请一块连续可用的空间,内存大小的单位是字节。 2.对于malloc函数,函数参数是字节,返回值是void*,在这里void*可以是int*,double*等,其他指针类型,这里由使用者自己来决定。 3.当malloc函数返回一个空指针NULL的时候代表这个函数调用失败,所以我们一定要对malloc函数的返回值进行检查。 4.当我们参入的参数是0的时候,malloc开辟的空间是取决于编译器的。
2.1.2 free函数
void free(void* ptr)
1. 对于 free 函数,用来释放一块动态开辟的空间,对于不是动态开辟的空间不可用free来释放。 2. free 函数的参数是void* ptr,这里的参数根据使用者自己来决定。 3. 我们将一块空间free掉之后我们要把指向那块空间的指针置为空指针NULL,不然就会产生野指针的情况。
2.1.3 malloc和free的使用
malloc函数和free函数都包含于头文件<stdlib.h>中。我们通过下面的例子来看看如何使用malloc和free。
#include<stdio.h>
#include<stdlib.h>
int main(){
int *p=malloc(20);
for(int a=0;a<5;a++){
*(p+a)=a+1;
printf("%d ",*(p+a));
}
free(p);
p=NULL;
return 0;
}
我们在使用free的时候不可以只释放这个空间的一部分,这样操作是不被允许的。什么意思呢?
我们开辟了一块空间我们从(p+1)的地址处进行释放,将 p 到 p+1 这一块空间留下来这样是不被允许的。同时我们将malloc开辟的空间进行释放之后我们的那个指针依然指向那块空间,只是对那块空间没有使用权了。
我们在 free 一块空间的时候也容易犯另外一个错误:我们的指针不指向开始时的起始地址。
#include<stdio.h>
#include<stdlib.h>
int main(){
int *p=malloc(sizeof(int)*5)
int a=0;
for(a=0;a<5;a++){
*p=a+1;
P++;//程序走出for循环之后p指向的不是初始位置
}
free(p);
p=NULL;
return 0;
}
3.1 calloc和realloc
3.1.1 calloc函数
calloc函数和ralloc函数十分的像,也是开辟一块连续可用的空间,但是calloc会讲地址的内容初始化为0。
void* calloc(size_t num, size_t size)
1.calloc函数是用来开辟一个个数为num个,大小为size的连续可用的空间。 2.函数的参数1是空间的个数,参数2是每个num的大小。 3.函数的返回值是void *,根据用户自己的使用情况来定义。
我们通过一段代码来看一下calloc函数的使用情况:
#include<stdio.h>
#include<stdlib.h>
int main(){
int *p=calloc(5,sizeof(int));
int a=0;
for(a=0;a<5;a++){
printf("%d "*(p+a));
}
free(p);
p=NULL:
return 0;
}
这里输出的结果是:0 0 0 0 0
所以我们在申请一块空间并且要对其 进行初始化的时候用,calloc函数函是很方便的。
3.1.2 realloc函数
我们在动态开辟的时候,有时候要根据实际的情况对这段空间进行扩容,这个时候我们就要用到realloc函数。
void* realloc( void* ptr ,size_t size)
我们在调用realloc函数的时候会出现两种情况:
一. 在原有的空间后面有足够的空闲空间来接收我们要新开辟的空间。
二. 在原有的空间后面没有足够的空闲空间来接收我们新开辟的空间。
情况1: 我们会在原有的地址后面开辟新的空间,函数的返回值仍然是原始的地址。
情况2: 我们无法在原有的地址后面开辟新的空间,函数会在堆区寻找一块新的空间,这块空间可以接收原有的空间和新开辟的空间,返回新空间的初始地址。
当realloc函数在调用失败后就会返回一个空指针,所以我们在使用时要注意判断返回值是不是空指针。
#include<stdio.h>
#include<stdlib.h>
int main(){
int *ptr=(int *)malloc(100);
if(ptr!=NULL){
//业务处理
}else {return 1;}
//代码1:直接realloc后的返回值存在ptr中
ptr=(int *)realloc(ptr,1000);
//代码2:将realloc的返回值先存在一个指针p中,p不为NULL再p赋值给ptr
int *p=(int *)realloc(ptr,1000);
if(p!=NULL){
ptr=p;
} else {return 1;}
return 0;
}
4.1 动态内存的常见错误
4.1.1 对NULL的解引用操作
#include<stdio.h>
int main(){
int *p=(int *)malloc(INT_MAX/4);
*p=20;//当malloc函数开辟空间失败后九返回NULL。
return 0;
}
4.1.2 free动态开辟内存的一部分
#include<stdio.h>
int main(){
int *p=(int *)malloc(5*sizeof(int));
p++;
free(p);
return 0;
}
4.1.3 free的非法使用
#include<stdio.h>
int main(){
int *p=20;
free(p);
return 0;
}
4.1.4 对动态开辟的空间的越界访问
#include<stdio.h>
int main(){
int *P=(int *)malloc(5*sizeof(int));
int a=0;
for(a=0;a<=5;a++){
*(p+a)=a+1;
printf("%d ",*(p+a));
}
return 0;
}
4.1.5 在动态内存开辟之后没有进行free,导致内存泄漏
#include<stdio.h>
void test{
double *p=(double *)malloc(5*sizeof(double));
}
int main(){
test();
return 0;
}
4.1.6 对一块空间进行多次的free
#include<stdio.h>
int main(){
int *p=(int *)malloc(5*sizeof(int));
free(p);
free(p);
return 0;
}
以上就是我们在编程中容易出现的错误,需要我们注意。
ui