C语言动态内存管理malloc/calloc/realloc/柔性数组

介绍三个库函数,它们可以直接向内存申请特定大小的空间,然后就可以使用这些空间了。

这三个库函数分别是malloc  calloc  realloc

明明已经有结构体、数组、int、float、double等类型可以直接创建,向内存申请空间。为什么还要这三个库函数呢?

前者申请创建的内存大小是不能改变的,创建时是多少就是多少。而malloc、calloc和realloc申请的内存空间是可以改变的。如果感觉内存不合适,随时可以再加或者减。

前者创建的内存是在栈区,而通过malloc等函数申请的空间在堆区。用完之后还要通过free函数释放申请的空间。

malloc

void * malloc( size_t size);

以上是函数声明,malloc的返回类型是void *的指针,传的参数size表示想申请的内存大小,单位是字节。如果申请的内存太大超过栈区空间申请失败,则返回空指针。需要注意的是,在使用申请内存空间的时候,会用到这个返回的指针(解引用操作来赋值),如果返回的是空指针(申请失败),则导致程序崩溃。所以在使用的时候要检验一下,如果程序不为空指针再使用。

free

free(void * ptr)

free主要用于释放malloc、calloc和realloc申请的内存。free的参数是指针,这个指针必须是所申请存的初始地址。(如果不是,程序会崩溃)

int *p = (int *)malloc(40);
if (NULL != p)
{
  for(int i = 0; i < 10; i++)
   {
      *(p+i) = i;
   }
}
free(p);
p = NULL;

释放完申请的内存之后,p中还存有之前的地址,但指向的内容已经不再是可以使用的了,为了不让他变成野指针,必须手动把p置为空指针。

如果不适用free函数进行释放内存空间,那么会导致内存泄漏。

free中的指针如果是空指针,也不做任何处理。

申请动态内存使用后一定要记得用free函数进行释放,并且只能通过free函数释放!

calloc

void * calloc (size_t num,size_t size)

calloc有两个参数,返回类型也是指针。它也可以用来申请内存大小,表示申请num个size大小的内存。它的作用和malloc相似。调用之后返回一个申请内存的起始位置的指针。这个指针也可能是空指针(内存申请失败)。

calloc在申请内存后,会把元素初始化为0,而malloc和realloc不会。

realloc

void * realloc(void *ptr,size_t size)

realloc用于调节malloc、calloc和realloc申请的内存空间的大小。

realloc有两个参数,第一个参数是需要调节的内存的起始地址,第二个参数是size,表示需要申请的新内存空间大小。单位也是字节。返回类型也是指针。

但一般不把这个指针直接返回给需要调节的起始地址。

int main()
{
  int *p =(int*) malloc(p,40);
  if( p != NULL)
  { 
     for(int i = 0; i < 10; i++)
     {
        *(p+i) = i;
     }
  }
  //内存不够了
  int *ptr = (int*)realloc(p,80);
  if(ptr != NULL)
  {
     p = ptr;
  }
  //...
  free(p);
  p = NULL;
  return 0;
}

如果申请失败,那么返回的是空指针,直接把返回的ptr给p,会导致原有的p的40个字节都没了,数据丢失,所以要检验一下返回的ptr是否为空指针,不为空指针再给p。

用realloc申请内存空间有两种情况,一种是原来的内存空间往后扩张的空间足够,没有被占用。那么返回的地址ptr的地址就和p的地址相同。

第二种是p后面的内存不够再扩张40个字节了,已经被占用了。所以系统要在栈区找块别的完整的80个字节的空间,把原来已经申请的40个字节的数据拷贝放在新的空间开辟一块80个字节的内存。这个时候新的内存起始位置当然和p不一样了。所以要把返回的ptr赋值给p,让p找到新开辟的空间来使用。

使用完以后也要用free来释放内存,并把指针置为空指针。

柔性数组

柔性数组出现在结构体中

struct A
{
  int a;
  char b;
  int arr[0];
}hehe;

结构体的最后一个数组成员就是柔性数组。柔性数组的大小是可以变化的。它的内存不计入结构体的内存大小。结构体中柔性数组前面至少要有一个成员。用malloc动态分配内存时,分配的内存应该大于结构体大小。比如想要申请10个整型的柔性数组,在用malloc分配时,参数用之前的结构体大小再加上10个整型的大小。

(strcut A*)p = (struct A*)malloc(sizeof(hehe)+10*sizeof(int));
  for(int i = 0; i < 10; i++)
  {
     p->arr[i] = i;
  }
  free(p);

这样柔性数组成员arr相当于有了10个整型元素的连续空间。

如果不使用柔性数组,把数组换成一个指针arr,通过malloc也能分配一定的内存,但是使用柔性数组便于释放空间。它只需要释放一次就可以。而对于指针arr来说,用malloc和realloc分配几次就需要释放几次,操作有点麻烦,还可能出错。

第二,柔性数组是连续的内存空间,有利于减少内存碎片。(内存碎片:使用malloc申请新的内存空间,新申请的内存空间之中可能会产生一些空隙,这些空间未能被使用,就是被浪费掉的空间,也就是内存碎片。)

这些是用柔性数组的优点。

你可能感兴趣的:(c语言,开发语言)