以前相关笔记:C指针和堆空间、指针和堆空间。
C动态分配的实际大小
假设从《The C Programming Language》中推测正确,从未经动态分配的堆内存呈现上图形式。不连续的堆内存以“链”的形式联系:Heap1 -> Heap2 ->Heap3 ->Heap4->Heap1。笔记将构成“堆链”的每个堆内存(如Heap1)称为“堆块”。malloc()/free()将每个堆块看作由两部分构成:“Header”和“可用堆内存”。在Header中包含了“指向下一个堆内存块的指针”、“本堆块的大小”。这样malloc()/free()就能更好地管理堆。
由于Header的构成的内存对齐,C中malloc(n)函数分配的堆内存会大于等于Header + n。
#define malloc_no_free(type, type_str) \ { \ printf("%s\tsizeof(%s)=%d\t", type_str, type_str, sizeof(type));\ char i; \ type *pthis = NULL, *plast = NULL; \ for(i = 0; i < 6; ++i){ \ if( NULL != (pthis = (type *)malloc(sizeof(type))) ){ \ if(plast) \ printf("%d\t", (int)pthis - (int)plast);\ plast = pthis;/*a litte logic to p*/ \ /*free(pthis);*/ \ pthis = NULL; \ } \ } \ printf("\n\n"); \ }
将此宏在主函数中调用:
struct _sc{ char c; }; struct _sic{ int i; char c; }; struct _sip{ int i; struct _sip *p; }; struct _sdc{ double d; char c;}; struct _s17{ char a[17]; }; int main() { malloc_no_free(char, "char"); malloc_no_free(int, "int"); malloc_no_free(double, "double"); malloc_no_free(struct _sc, "_sc"); malloc_no_free(struct _sic, "_sic"); malloc_no_free(struct _sip, "_sip"); malloc_no_free(struct _sdc, "_sdc"); malloc_no_free(struct _s17, "_17"); return 0; }
Figure3:程序运行结果
分析运行结果:
(1) 如果将宏中” free(pthis);”的注释去掉,则所有的地址差都为0。这恰好验证了free()堆地址的机制:假设将要被释放的堆内存的地址为p,free(p)将以p为首地址的堆内存块释放到堆链中的某个地址上,且此地址跟p值最邻近,这样就可以保证尽可能的使堆内存以大块的形式存在而不至于让堆内存成为碎片。
(2) 将” free(pthis);”注释
从这里可以看出:Header所占堆内存大小为8字节,堆内存对齐也为8(会看内存对齐就不难观察出来)。
造就实际分配内存大于用户所需原因:
typedef long Align; union header { struct { union header *ptr; unsigned size; } s; Align x; };
《C圣经》中想用Align(long)作为内存对齐的值,但由于win32上sizeof(long) = 4,由于sizeof(union header) = sizeof(s) = 8,故而在malloc()返回的堆内存中存储数据的内存对齐数值为8。
对于C中的malloc(n)分配,有以下进一步的结论: