详解C语言中的动态内存管理

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

详解C语言中的动态内存管理

小马学习代码   2022-12-12 我要评论

一、动态内存管理

1.1为什么要有动态内存管理

1.1.1  在c语言中我们普通的内存开辟是直接在栈上进行开辟的 

int i = 20;//在栈空间上开辟四个字节
int arr[10]={0}; //在栈中连续开辟四十个字节

这样开辟的特点是:

(1)他所开辟的空间是固定的 

(2)数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配

但对于空间的需求,我们有的时候并不知道,有可能空间开大了造成了浪费,也有可能空间开小了造成栈溢出,这样我们就需要一个动态的内存管理让我们需要多少内存的时候开辟多少。

1.2动态内存介绍

1.2.1malloc 和 free

void*   malloc  (size_t size);

这个函数想内存中申请一个连续的空间(是在堆中申请),并返回指向这块空间的指针。 

如果开辟成功,则返回一个指向开辟好空间的指针。

如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。

返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己 来决定。

如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

同样的C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的

void * free (void * ptr) 

free 是用来释放动态开辟的内存的 

如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。

如果参数 ptr 是NULL指针,则函数什么事都不做。

#include<stdio.h>
#include<stdlib.h>  //malloc 和free 都在stdlib.h的头文件里
int main()
{
    int arr[10] ={0}; //这是在栈中申请连续的四十个空间 是静态的
    int * arr1;
    int *ptr ;
    ptr =(int*)malloc (10*sizeof(int)); //申请一个动态内存空间为40字节
    if(ptr==NULL) //防止申请空间失败传入了空指针
    {
        perror("ptr");
    }
    arr1=ptr;
    free(arr1);  //结束后要进行一个空间的释放
    arr1=NULL;      //然后在指向空指针防止出现了野指针
    //这就是申请一个动态内存空间的套用过程
    
    return 0;
}

1.2.2 calloc

c语言同样的提供了一个函数calloc,也是用来动态内存的分配

void* calloc (size_t num, size_t size);

函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。

与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

#include<stdio.h>
#include<stdlib.h>  //malloc 和free 都在stdlib.h的头文件里
int main()
{
    int arr[10] ={0}; //这是在栈中申请连续的四十个空间 是静态的
    int * arr1;
    int *ptr ;
    ptr =(int*)calloc (10,sizeof(int)); //申请一个动态内存空间为40字节
    if(ptr==NULL) //防止申请空间失败传入了空指针
    {
        perror("ptr");
    }
    arr1=ptr;
    free(arr1);  //结束后要进行一个空间的释放
    arr1=NULL;      //然后在指向空指针防止出现了野指针
    //这就是申请一个动态内存空间的套用过程
    
    return 0;
}

1.2.3 realloc

realloc 使我们申请的的动态内存空间变得灵活,在申请动态内存空间的时候,有时候我们申请的过大,或者申请的过小的时候,我们可以通过realloc也对我们申请的空间进行一个合理的调整改变

void* realloc (void* ptr, size_t size);

  • ptr 是要调整的内存地址
  • size 调整之后新大小
  • 返回值为调整之后的内存起始位置

这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到 新 的空间。

这有两种调节:

第一种是在你原来的内存上进行了一个改变(内存改变不大),就是在原有的内存空间进行加大空间。

第二种就是原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。

#include<stdio.h>
#include<stdlib.h>  //malloc 和free 都在stdlib.h的头文件里
int main()
{
    int arr[10] ={0}; //这是在栈中申请连续的四十个空间 是静态的
    int * arr1;
    int *ptr ;
    ptr =(int*)calloc (10,sizeof(int)); //申请一个动态内存空间为40字节
    if(ptr==NULL) //防止申请空间失败传入了空指针
    {
        perror("ptr");
    }
    arr1=ptr;
    arr1 =(int*)realloc (arr1,10000);  //改变原有的内存空间
    free(arr1);
    arr1=NULL;
    ptr=NULL; 
    return 0;
}

1.3常见的动态内存错误

1.3.1对NULL指针解引用操作

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int *ptr;
    ptr=(int*)malloc(sizeof(int));
    *ptr=1;  //这里有可能申请失败 ,但我这没有失败,为了以防万一还是需要进行判断一下,正确的申请在上面
    free(ptr);
    ptr=NULL;
    
    
    return 0;
}

1.3.2对动态内存的越界

#include<stdio.h>
#include<stdlib.h>  //malloc 和free 都在stdlib.h的头文件里
int main()
{
    int *ptr ;
    ptr =(int*)malloc(40); //申请一个动态内存空间为40字节
    if(ptr==NULL) //防止申请空间失败传入了空指针
    {
        perror("ptr");
    }
    
    for(int i=0;i<=11;i++)
    {
        *(ptr+i)=i;   //申请的是四十个字节,这里产生了越界
    }
    for(int i=0;i<=11;i++)
    {
        printf("%d ",*(ptr+i));
    }
    free(ptr);
    ptr=NULL;
    return 0;
}

1.3.3对非动态空间进行释放

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int *p;
    *p=10;  
    free(p);  //这里的p并不是动态内存空间仍然进行了释放
    return 0;
}

1.3.4 动态内存空间的部分释放

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int *p=(int*)malloc(sizeof(int)*2);
    if(p==NULL)
    {
        perror("p");
    }
    p++;
    free(p); //这里的p的地址并不是起始地址,只是进行了部分的释放
    p=NULL;
   
}

1.3.5对一块动态内存进行多次释放

#include<stdio.h>
#include<stdlib.h>
int main()
{
    int *p=(int*)malloc(sizeof(int)*2);
    if(p==NULL)
    {
        perror("p");
    }
    
    free(p);
   // ~~~~~~~~~~
    free(p);  //  已经释放p了有进行了释放
    p=NULL;
   
}

这个真的有可能发生,当我们代码写的比较长的时候,我们有可能忘了我们是否已经释放这块空间,就有可能进行重复的释放,这是不正确的,而解决他的方法是,当我们释放了一块空间后,一定让他指为空指针。

1.3.6动态内存忘记释放(内存泄漏)

#include<stdio.h>
#include<stdlib.h>
void test(int *p)
{
     p=(int*)malloc(sizeof(int)*2);
    if(p==NULL)
    {
        perror("p");
    }
}
int main()
{
    int *ptr;
    test(ptr); //这里就是没有对内存进行释放
   
}

总结:

对于动态内存还是比较重要的,因为堆的空间是比栈的空间的是大的,同时我们要知道,动态的是可以进行修改的,我们需要多少内存就可以开辟多少内存,防止了内存的浪费,但是我们在申请动态内存的时候一定要防止一些不必要的错误不然就会得不偿失。

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们