概述
在c
语言中,堆区可以自由被开发者掌控。高度的自由也需要带来高额的管理成本,如申请后需要手动释放,本文是笔者学习堆区的笔记。
malloc和free
malloc:
void *malloc(
size_t size
);
参数:
size
申请堆内存大小。
返回:
成功返回内存地址,失败返回NULL
macllo
内存格式:
(注意博主在vs2015中内存结构如上图所示,不同编译器有所不同)
堆类别决定系统如何管理这个块内存。
#include<stdlib.h>
int main(void) {
char *intP = (char*)malloc(sizeof(char) * 2);
int *intP2 = (int*)malloc(sizeof(int) * 2);
free(intP);
free(intP2);
}
我们首先查看intP
内存结构图:
上图堆类别写错了,这里1是指这个堆是正常类别的堆。
我们可以从右侧观察窗口看到intP
保存的内存地址就是内存堆栈中实际使用地址即0x010e8a78
。而补充的对地址和实际使用地址相差0x20
大小。
我们查看一下intP2
内存的地址为0x010e4950
,intP
指向的下一个对地址为0x010E4930
,当0x010E4930
+0x20
=intP2
。
下图为intP2
内存结构图:
可以看到intP2
上一个堆地址加上20偏移正好就是IntP
。
如果开发者想在堆中添加调试信息请用如下api:
void *_malloc_dbg(
size_t size,
int blockType,
const char *filename,
int linenumber
);
blockType
是对堆的类别
枚举如下:
#define _FREE_BLOCK 0
#define _NORMAL_BLOCK 1
#define _CRT_BLOCK 2
#define _IGNORE_BLOCK 3
#define _CLIENT_BLOCK 4
#define _MAX_BLOCKS 5
#define _UNKNOWN_BLOCK (-1)
举例说明:
#include<stdlib.h>
#include<crtdbg.h>
int main(void) {
char *intP = (char*)_malloc_dbg(sizeof(char) * 2,1,__FILE__,__LINE__);
free(intP);
}
我们跳转到对应的常量池地址
free:
void free(
void *memblock
);
释放memblock
指向的内存。
我们看下free释放前后的区别:
释放前:
释放后:
同时修改上下级堆链表的内存,这里就不做讲解了。(堆内存结构会存储一个上一个堆地址和下一个堆地址,当删除一个堆的时候会进行链表删除操作)
calloc和realloc
calloc
void *calloc(
size_t number,
size_t size
);
申请number*size
的内存大小
#include<stdlib.h>
#include<crtdbg.h>
#include<stdio.h>
int main(void) {
int *p = (int *)calloc(1, sizeof(int));
free(p);
getchar();
}
realloc
重新给指向的地址分配新的大小。
#include<stdlib.h>
#include<crtdbg.h>
#include<stdio.h>
int main(void) {
char* intP = (char*)malloc(4);
char *p = (char *)realloc(intP, 3);
free(p);
getchar();
}
注意realloc
会拷贝旧内存数据到新内存地址,而后释放旧堆。并且如果新生申请内存大小大于旧的大小。不管是缩小还是放大新堆都容易引起内存碎片问题