C中 malloc()分配堆内存实际的大小

转载自:http://blog.csdn.net/misskissc/article/details/17717717

C动态分配的实际大小

1堆内存


Figure1:内存中的堆内存空间

假设从《The  C  Programming  Language》中推测正确,从未经动态分配的堆内存呈现上图形式。不连续的堆内存以“链”的形式联系:Heap1 -> Heap2 ->Heap3 ->Heap4->Heap1。笔记将构成“堆链”的每个堆内存(如Heap1)称为“堆块”。malloc()/free()将每个堆块看作由两部分构成:“Header”和“可用堆内存”。在Header中包含了“指向下一个堆内存块的指针”、“本堆块的大小”。这样malloc()/free()就能更好地管理堆。


2 堆内存分配

[1] mallco()分配机制

根据C中malloc(n)函数动态分配堆的机制:分配堆内存的时候就依序由低到高的地址搜索“堆链”中的堆块,搜索到“可用堆内存”满足n的堆块(如Heap1)为止。若Heap1的“可用堆内存”刚好满足n,则将Heap1从“堆链”中删除,同时重新组织各堆块形成新的“堆链”;若Heap1的“可用堆内存”大小大于n,则将malloc(n)申请到的“Header” + "可用堆内存"部分从Heap1中分裂,将剩余的Heap1堆内存块重新加入“堆链”中。经分裂后的堆内存也包含“Header”和“可用堆内存”两部分(如图Figure 2),然后将由malloc()分配得到的“可用堆内存”返回给用户。 若某块堆内存空间比较大(如Heap1),能够满足较小内存的多次申请,那么由malloc(n)多次申请的堆内存块都是连续被分配给用户的(因为有Header,所以用户使用的堆地址不连续)

为方便free()释放堆空间,经malloc(n)分配给用户的堆空间也隐含一个Header。如下图所示:

Figure2:malloc()分配的堆内存结构

由于Header的构成的内存对齐,C中malloc(n)函数分配的堆内存会大于等于Header + n。


[2] 程序验证

为了测试C语言malloc()函数所分配堆的实际大小(Header 大小和内存对齐),编写了如下宏:
[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. #define malloc_no_free(type, type_str)                          \  
  2.         {                                       \  
  3.             printf("%s\tsizeof(%s)=%d\t", type_str, type_str, sizeof(type));\  
  4.             char i;                             \  
  5.             type *pthis = NULL, *plast = NULL;                  \  
  6.             for(i = 0; i < 6; ++i){                      \  
  7.                 if( NULL != (pthis = (type *)malloc(sizeof(type))) ){   \  
  8.                     if(plast)                       \  
  9.                         printf("%d\t", (int)pthis - (int)plast);\  
  10.                     plast   = pthis;/*a litte logic to p*/      \  
  11.                     /*free(pthis);*/                    \  
  12.                     pthis   = NULL;                 \  
  13.                 }                               \  
  14.            }                                    \  
  15.            printf("\n\n");                          \  
  16.         }  
  • 用宏写这一段函数式为了在C中能够传递任何数据类型的变量。
  • 如果传递给malloc()函数的参数较小,那么malloc()函数是极有可能在图中的Heap1对区域进行多次分配的,如此这段代码就能够测试出malloc()函数分配堆内存的实际大小。

将此宏在主函数中调用:

[cpp]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. struct _sc{ char c; };  
  2. struct _sic{ int i; char c; };  
  3. struct _sip{ int i; struct _sip *p; };  
  4. struct _sdc{ double d; char c;};  
  5. struct _s17{ char a[17]; };  
  6.   
  7. int main()  
  8. {  
  9.     malloc_no_free(char"char");  
  10.     malloc_no_free(int"int");  
  11.     malloc_no_free(double"double");  
  12.     malloc_no_free(struct _sc, "_sc");  
  13.     malloc_no_free(struct _sic, "_sic");  
  14.     malloc_no_free(struct _sip, "_sip");  
  15.     malloc_no_free(struct _sdc, "_sdc");  
  16.     malloc_no_free(struct _s17, "_17");  
  17.     return 0;  
  18. }  

在codeblocks + GNU GCC compiler下编译通过并运行得到一下结果:


Figure3:程序运行结果

分析运行结果:

(1) 如果将宏中” free(pthis);”的注释去掉,则所有的地址差都为0。这恰好验证了free()堆地址的机制:假设将要被释放的堆内存的地址为p,free(p)将以p为首地址的堆内存块释放到堆链中的某个地址上,且此地址跟p值最邻近,这样就可以保证尽可能的使堆内存以大块的形式存在而不至于让堆内存成为碎片。

(2) 将” free(pthis);”注释

  • 各结构体所占内存大小再一次体现了内存对齐
  • 在一大块堆之上用malloc()函数分配内存,真实得到的内存要比实际大。得到的真实内存块的大小为8(实为内存对齐大小值)的整数倍。当数据结构所占内存为16的倍数时,malloc()分配的内存会多出8个字节,这8个字节就是header。(将struct _s17内数组维数改为16倍数时可验证)。

从这里可以看出:Header所占堆内存大小为8字节,堆内存对齐也为8(会看内存对齐就不难观察出来)。


3 动态分配内存对齐

造就实际分配内存大于用户所需原因

  • 每次malloc(n)分配的堆内存由Header + n构成。
  • Header造就的内存对齐。

[1] Header

《C 圣经》的第八章”The  UNIX System Interface”的第七节”Example – A Strorage Allocator”中用以下结构体描述了Header:
[cpp]  view plain  copy
 print ?
  1. typedef long Align;  
  2. union header {  
  3.     struct {  
  4.         union header *ptr;  
  5.         unsigned size;  
  6.     } s;  
  7.     Align x;  
  8. };  
union  header用其成员s.ptr作为指向堆链表中下一个堆块,s.size保存此堆的大小。x本意是用于内存对齐。 malloc()/free()就是通过以堆内存为存储空间隐藏于用户的Header结构体来合理地管理堆内存的

[2] 内存对齐

《C圣经》中想用Align(long)作为内存对齐的值,但由于win32上sizeof(long) = 4,由于sizeof(union header) = sizeof(s) = 8,故而在malloc()返回的堆内存中存储数据的内存对齐数值为8。


所以,以上为每种数据类型所分配堆空间实际大小就得到了合理的解释(排除第一个为32 bytes):

Figure4:C中实际的动态分配
所以在适当的明白malloc()的实际分配之后,在以后程序中的动态分配过程中就要利用这个过程避免堆内存的浪费:许多单独的堆内存块可以合并在一起分配,这样程序在运行过程中使用的堆内存会减少,有利于程序压缩空间。

4 malloc()分配总结

对于C中的malloc(n)分配,有以下进一步的结论:

  • 实际分配的堆内存是Header + n结构。返回给用户的是n部分的首地址。
  • 由于内存对齐值8,实际分配的堆内存大于等于sizeof(Header) + n。
所以在编程过程中,需要用内存对齐的知识合理的让malloc()分配的内存变得小一些。

C Note Over。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值