欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

动态内存申请函数:【malloc】【 free】,【calloc 】,【realloc】

程序员文章站 2022-05-11 20:28:36
...

为什么存在动态内存分配?

局部变量在栈区。
动态内存开辟在堆区。
全局变量在静态区。

我们已经掌握的内存开辟方式有:

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

特点:
1.空间开辟的大小是固定的。
2.数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
有时候我们需要空间的大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。

这个时候我们需要了解动态内存开辟。
动态内存函数的介绍:

1. C语言提供了一个动态内存开辟的函数:malloc

malloc 动态内存申请

void *malloc(size_t size)
int * p = (int *)malloc(10*sizeof(int)); //在堆区开辟10个整型空间

这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
-如果开辟成功,则返回一个指向开辟好空间的指针。
-如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
-返回值的类型是void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
-如果参数size 为0,malloc的行为是标准未定义的,取决于编译器。

C

2. C语言提供了一个函数:free

void free(void* ptr);
专门用来做动态内存释放和回收。
-如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。
-如果参数ptr是空指针,则函数什么事都不做。

#include<errno.h>
#include<stdlib.h>
#include<string.h>
int main()
{
   int *p  =(int *)malloc(40);
   int i = 0;
   if(p == NULL)  //判断p是否为空指针
   {
      printf("%s\n",strerror(errno));
      return 0;  //若开辟失败,那么在这返回。如果在这没返回,说明p不为NULL,则开辟成功。
   }
   //开辟成功,开始使用
   for(i=0; i<10; i++)  //初始化这块空间
   {
      *(p+i) = i;
   }
   for(i=0; i<10; i++)
   {
      printf("%d ",p[i]);
   }
   //释放空间
   free(p);
   return 0;
}

动态内存申请函数:【malloc】【 free】,【calloc 】,【realloc】
注意
malloc和free 都声明在stdlib.h头文件中。
malloc和free必须成对使用。

3. C语言提供了一个函数:calloc

用来动态内存分配。

void* calloc(size_t num,size_t size)
num 是元素个数
size 是每个元素的长度,单位是字节。

动态内存申请函数:【malloc】【 free】,【calloc 】,【realloc】

函数功能是为num 个大小为size的元素开辟一块空间,并把空间的每个字节初始化为0.
与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为全0.

举个例子:

 #include<stdio.h>
 #include<stdlib.h>
 int main()
 {
     int *p = (int *)calloc(10,sizeof(int)); //开辟了10个整形空间
     int i = 0;
     if(NULL != p)
     {
        for(i=0; i<10; i++)
        {
           *(p+i) = i;
        }
        for(i=0; i<10; i++)
        {
        printf("%d ",p[i]);
        }
     }
     free(p);
     p = NULL;
     return 0;
 }

动态内存申请函数:【malloc】【 free】,【calloc 】,【realloc】
如果是malloc ,在没有初始化的情况下则会输出:随机值。
动态内存申请函数:【malloc】【 free】,【calloc 】,【realloc】
这就是两者的区别。除此之外函数参数形式上也有所差异(malloc一个参数,calloc两个参数),但也并不难理解。

相比下来,两者之间malloc效率较高,因为malloc没有初始化。
那么主动初始化也是calloc的一个优点,如何选择得看程序员自己的需求。

4. realloc :重新开辟空间

动态内存申请函数:【malloc】【 free】,【calloc 】,【realloc】

realloc函数的出现,让动态内存管理更加灵活。
有时候我们会发现过去申请的空间太小了,有时候又会觉得大了,那为了合理的使用内存,我们一定会对内存的大小做灵活的调整。那realloc函数就可以做到对动态开辟内存大小的调整。

函数原型:

void* realloc (void *ptr,size_t size);
- ptr (指针指向之前开辟好的空间)是要调整的内存地址。
- size 新开辟空间的大小,单位是字节。
- void* 返回void型的指针,指向重新开辟的内存块。
- 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
- realloc 在调整内存空间是存在两种情况:

动态内存申请函数:【malloc】【 free】,【calloc 】,【realloc】
情况1:原有空间之后有足够大的空间
此时,要扩展内存就直接原有内存之后追加空间,原来空间的数据不发生变化。 返回原来地址。
情况2:原有空间之后没有足够大空间
此时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。

int main()
{
   int *p  =(int *)calloc(10,sizeof(int));
   int *tmp = NULL; //重新定义一个指针接收新的空间
   int i = 0;
   if(p == NULL)  
   {
      printf("%s\n",strerror(errno));
      return 0;  
   }
   //开辟成功,开始使用
   for(i=0; i<10; i++)
   {
      *(p+i) = i;
   }
   tmp = (int*)realloc(p,20*sizeof(int));//重新开辟的空间
   if(tmp != NULL) 
   {
       p = tmp;
   }
   for(i=0; i<20; i++)
   {
      printf("%d ",*(p+i));
   }
   //释放空间
   free(p);
   system("pause");
   return 0;
}

使用总结:

  1. realloc失败的时候,返回NULL
  2. realloc失败的时候,原来的内存不改变,不会释放也不会移动
  3. 假如原来的内存后面还有足够多剩余内存的话,realloc的内存=原来的内存+剩余内存,realloc还是返回原来内存的地址; 假如原来的内存后面没有足够多剩余内存的话,realloc将申请新的内存,然后把原来的内存数据拷贝到新内存里,原来的内存将被free掉,realloc返回新内存的地址
  4. 如果size为0,效果等同于free()。这里需要注意的是只对指针本身进行释放,例如对二维指针**a,对a调用realloc时只会释放一维,使用时谨防内存泄露。
  5. 传递给realloc的指针必须是先前通过malloc(), calloc(), 或realloc()分配的。
  6. 传递给realloc的指针可以为空,等同于malloc。