C中堆管理―浅谈malloc,calloc,realloc

本文深入探讨了C语言中用于内存管理的关键函数:malloc、calloc、realloc与alloca,详细解释了它们的工作原理、区别及使用场景。包括内存分配、初始化、释放等操作,旨在帮助开发者掌握高效利用内存的技术。

malloc:

原型:extern void *malloc(unsigned int num_bytes);
  头文件:在TC2.0中可以用malloc.h或 alloc.h (注意:alloc.h 与 malloc.h 的内容是完全一致的),而在Visual C++6.0中可以用malloc.h或者stdlib.h。
  功能:分配长度为num_bytes字节的内存块
  返回值:如果分配成功则返回指向被分配内存的指针(此存储区中的初始值不确定),否则返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。函数返回的指针一定要适当对齐,使其可以用于任何数据对象。

void *malloc(size_t size);
  说明:malloc 向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。
  备注:void* 表示未确定类型的指针,更明确的说是指申请内存空间时还不知道用户是用这段空间来存储什么类型的数据(比如是char还是int或者...)
  从函数声明上可以看出。malloc 和 new 至少有两个不同: new 返回指定类型的指针,并且可以自动计算所需要大小。比如:
  int *p;
  p = new int; //返回类型为int* 类型(整数型指针),分配大小为 sizeof(int);
  或:
  int* parr;
  parr = new int [100]; //返回类型为 int* 类型(整数型指针),分配大小为 sizeof(int) * 100;
  而 malloc 则必须要由我们计算字节数,并且在返回后强行转换为实际类型的指针。
  int* p;
  p = (int *) malloc (sizeof(int)*128);//分配128个(可根据实际需要替换该数值)整型存储单元,并将这128个连续的整型存储单元的首地址存储到指针变量p中
  double *pd=(double *) malloc (sizeof(double)*12);//分配12个double型存储单元,并将首地址存储到指针变量pd中
  第一、malloc 函数返回的是 void * 类型。对于C++,如果你写成:p = malloc (sizeof(int)); 则程序无法通过编译,报错:“不能将 void* 赋值给 int * 类型变量”。所以必须通过 (int *) 来将强制转换。而对于C,没有这个要求,但为了使C程序更方便的移植到C++中来,建议养成强制转换的习惯。
  第二、函数的实参为 sizeof(int) ,用于指明一个整型数据需要的大小。如果你写成:
  int* p = (int *) malloc (1);
  代码也能通过编译,但事实上只分配了1个字节大小的内存空间,当你往里头存入一个整数,就会有3个字节无家可归,而直接“住进邻居家”!造成的结果是后面的内存中原有数据内容被改写。

工作机制:

malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到连接表上。调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块。如果无法获得符合要求的内存块,malloc函数会返回NULL指针,因此在调用malloc动态申请内存块时,一定要进行返回值的判断。

calloc

calloc是一个C语言函数

  函数名: calloc

  void *calloc(unsigned n,unsigned size);

  功 能: 在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。

  跟malloc的区别:calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据。

realloc

函数简介

  原型:extern void *realloc(void *mem_address, unsigned int newsize);

  语法:指针名=(数据类型*)realloc(要改变内存大小的指针名,新的大小)。//新的大小一定要大于原来的大小不然的话会导致数据丢失!

  头文件:#include <stdlib.h> 有些编译器需要#include <malloc.h>,在TC2.0中可以使用alloc.h头文件

  功能:先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域,同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。

  返回值:如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。 

  注意:这里原始内存中的数据还是保持不变的。当内存不再使用时,应使用free()函数将内存块释放。

 _alloca:

函数原型为:void * __cdecl alloca(size_t);

内存分配函数,与malloc,calloc,realloc类似.
  但是注意一个重要的区别,_alloca是在栈(stack)上申请空间,用完马上就释放.
  包含在头文件malloc.h中.
  在某些系统中会宏定义成_alloca使用.

原文地址:http://tc.itkee.com/developer/detail-1ab4.html

內存區域可以分爲棧,堆,靜態存儲區和常量存儲區。局部變量,函數形參,臨時變量都是在棧上獲得內存的,它們獲取的方式都是由編譯器自動執行的。

      C 標準函數庫提供了許多函數來實現對堆上內存管理,其中包括:malloc函數,free函數,calloc函數和realloc函數。使用這些函數需要包含頭文件stdlib.h

1.   malloc函數

malloc函數可以從堆上獲得指定字節的內存空間,其函數聲明如下:

void * malloc(int n);

其中,形參n爲要求分配的字節數。如果函數執行成功,malloc返回獲得內存空間的首地址;如果函數執行失敗,那麼返回值爲NULL。由於 malloc函數值的類型爲void型指針,因此,可以將其值類型轉換後賦給任意類型指針,這樣就可以通過操作該類型指針來操作從堆上獲得的內存空間。

     需要注意的是,malloc函數分配得到的內存空間是未初始化的。因此,一般在使用該內存空間時,要調用另一個函數memset來將其初始化爲全0。memset函數的聲明如下:

    void * memset (void * p,int c,int n) ;

       該函數可以將指定的內存空間按字節單位置爲指定的字符c。其中,p爲要清零的內存空間的首地址,c爲要設定的值,n爲被操作的內存空間的字節長度。如果要用memset清0,變量c實參要爲0。malloc函數和memset函數的操作語句一般如下:

int * p=NULL;

p=(int *)malloc(sizeof(int));

if(p==NULL)

    printf(“Can’t get memory!\n”);

memset(p,0,siezeof(int));

注意:通過malloc函數得到的堆內存必須使用memset函數來初始化。

demo:

#include

#include

#include

int main(){

     int * p=NULL;

     p=(int *)malloc(sizeof(int));

     if(NULL==p){

         printf("Can't get memory!\n");

         return -1;

     }

     printf("%d\n",*p);           //輸出分配的空間上的值

     memset(p,0,sizeof(int));     //將p指向的空間清0

     printf("%d\n",*p);           //輸出調用memset函數後的結果

     *p=2;

     printf("%d\n",*p);

     return 0;

運行結果截圖:

C中堆管理――淺談malloc,calloc,realloc函數之間的區別

2. free函數

       從堆上獲得的內存空間在程序結束以後,系統不會將其自動釋放,需要程序員來自己管理。一個程序結束時,必須保證所有從堆上獲得的內存空間已被安全釋放,否則,會導致內存泄露。例如上面的demo就會發生內存泄露。

free函數可以實現釋放內存的功能。其函數聲明爲:

void free (void * p);

    由於形參爲void指針,free函數可以接受任意類型的指針實參。

    但是,free函數只是釋放指針指向的內容,而該指針仍然指向原來指向的地方,此時,指針爲野指針,如果此時操作該指針會導致不可預期的錯誤。安全做法 是:在使用free函數釋放指針指向的空間之後,將指針的值置爲NULL。因此,對於上面的demo,需要在return

語句前加入以下兩行語句:

free(p);

p=NULL;

注意:使用malloc函數分配的堆空間在程序結束之前必須釋放。

3. calloc函數

calloc函數的功能與malloc函數的功能相似,都是從堆分配內存。其函數聲明如下:

void *calloc(int n,int size);

函數返回值爲void型指針。如果執行成功,函數從堆上獲得size X n的字節空間,並返回該空間的首地址。如果執行失敗,函數返回NULL。該函數與malloc函數的一個顯著不同時是,calloc函數得到的內存空間是 經過初始化的,其內容全爲0。calloc函數適合爲數組申請空間,可以將size設置爲數組元素的空間長度,將n設置爲數組的容量。

demo:

#include

#include

#define SIZE 5

int main(){

     int * p=NULL;

     int i=0;

     //爲p從堆上分配SIZE個int型空間

     p=(int *)calloc(SIZE,sizeof(int));

    if(NULL==p){

         printf("Error in calloc.\n");

         return -1;

     }

     //爲p指向的SIZE個int型空間賦值

     for(i=0;i

         p[i]=i;

     //輸出各個空間的值

     for(i=0;i

         printf("p[%d]=%d\n",i,p[i]);

     free(p);

     p=NULL;

     return 0;

運行結果:

C中堆管理――淺談malloc,calloc,realloc函數之間的區別

提示:calloc函數的分配的內存也需要自行釋放。

4. realloc函數

realloc函數的功能比malloc函數和calloc函數的功能更爲豐富,可以實現內存分配和內存釋放的功能,其函數聲明如下:

void * realloc(void * p,int n);

其中,指針p必須爲指向堆內存空間的指針,即由malloc函數、calloc函數或realloc函數分配空間的指針。realloc函數將指針 p指向的內存塊的大小改變爲n字節。如果n小於或等於p之前指向的空間大小,那麼。保持原有狀態不變。如果n大於原來p之前指向的空間大小,那麼,系統將 重新爲p從堆上分配一塊大小爲n的內存空間,同時,將原來指向空間的內容依次複製到新的內存空間上,p之前指向的空間被釋放。relloc函數分配的空間 也是未初始化的。

注意:使用malloc函數,calloc函數和realloc函數分配的內存空間都要使用free函數或指針參數爲NULL的realloc函數來釋放。

demo:

#include

#include

int main(){

     int * p=NULL;

     p=(int *)malloc(sizeof(int));

     *p=3;

     printf("p=%p\n",p);

     printf("*p=%d\n",*p);

     p=(int *)realloc(p,sizeof(int));

     printf("p=%p\n",p);

     printf("*p=%d\n",*p);

     p=(int *)realloc(p,3*sizeof(int));

     printf("p=%p\n",p);

     printf("*p=%d",*p);

    //釋放p指向的空間

     realloc(p,0);

     p=NULL;

     return 0;

運行結果:

C中堆管理――淺談malloc,calloc,realloc函數之間的區別

從結果中,你看到了什麼?

注意:如果要使用realloc函數分配的內存,必須使用memset函數對其內存初始化


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值