一向对内存敏感性不好,或者说不太有sense,基础恶补恶补很有必要,结合刚看了下一个日本人写的关于malloc的讲解,理清下思路
malloc()是根据参数指定的尺寸来分配内存块,返回指向内存块初始位置的指针,经常用于动态分配结构体的内存领域,分配执行前还不知道大小的数据的内存区域等
p = malloc(size); free(p);
一旦内存分配失败(内存不足?),malloc()会返回NULL;利用malloc()分配的内存结束使用的时候,通过free()来释放内存
这种能够动态运行时进行内存分配,并且可以通过任意的顺序释放的记忆区域,称为heap堆
动态分配结构体
假如要用下面的结构体管理一本书的数据
typedef struct { char title[64]; int price; char isbn[32]; ...... }BookData;
那么对于要管理很多书来说,肯定就要管理超多的BookData;这样虽然可以通过一个巨大的数组来管理大量的BookData,但是数组必须要定义长度,这也是一件头疼的事,太大了浪费空间,太小了可能就会不足;通过下面这种方式,可以在运行时候分配BookData内存区域
BookData *book_data_p; book_data_p = (BookData *)malloc(sizeof(BookData));
进而如果用链表来管理,可以保持任意个数的BookData,只要内存足够
typedef struct BookData_tag{ char title[64]; int price; char isbn[32]; ...... struct BookData_tag *next; }BookData;
通过next指向下一个BookData的指针,就形成了链状数据结构,保存大量的BookData
分配可变长数组
上面书名的地方是通过char title[64];来定义的,但是书名超过了64长度,就放不下了;但是也不是所有书名都有这么长,所以太长也会浪费,于是可以将title声明为
char *title;
这样可以给标题字符串分配内存区域
BookData *book_data_p; ...... book_data_p->title = malloc(sizeof(char) * len);
这里len为标题字符数+1,访问标题某个字符只需要book_data_p->title[i]即可
malloc()实际上是一个标准库函数,而不是系统调用;malloc从操作系统一次性取得比较大的内存,然后将这些内存零售给应用程序,在UNIX下使用brk()系统调用来取得内存;在前面打印变量地址的时候,main函数里的变量和参数变量地址之间空出了一片很大范围的内存区域;系统调用brk()通过设定这个内存区域的末尾地址,来伸缩内存空间的函数;调用函数的时候,栈会向地址小的一边伸长,多次调用malloc()的时候,会调用一次brk(),内存区域会向地址大的一边伸长
说实话说的跟天书一样,下面随手画一个图可以看到malloc()的链表实现
管理区域 | 使用中的块 |
管理区域 | 空块 |
管理区域 | 使用中的块 |
管理区域 | 空块 |
管理区域 | 使用中的块 |
管理区域 | 使用中的块 |
…………………………………………如果区域不足,请求操作系统扩充…………………………………
在每个块之前加了一个管理区域,每个管理区域保存了下面一个块的地址,这样就通过管理区域构建了一个链表
malloc()遍历链表寻找空的块,如果发现尺寸大小能够满足使用的块,就分割出来将其变成试用中的块,并且向应用程序返回紧邻管理区域的后面区域的地址;free()将管理区域的标记改写成空块,顺便也将上下空的块合并成一个块,这样可以防止块碎片化
假如不存在足够大的空块,那么就请求操作系统对空间进行扩充,比如UNIX使用brk()系统调用
free()
调用free()之后,对应的内存区域不会立马还给操作系统,下面简单例子
#include <stdio.h> #include <stdlib.h> int main(){ int *ptr; ptr = malloc(sizeof(int)); *ptr = 12345; free(ptr); printf("*ptr = %d\n", *ptr); return 0; } lihui@2015 /cygdrive/d/work $ ./a.exe *ptr = 12345
如此可见调用free之后,对应内存并没有立马被破坏,所以调用free之后,不要引用对应的内存区域
比如某内存区域被两个指针A和B同时引用,而此时A认为当前区域对他来说不需要,就free掉了,而此刻B还引用着这块内存区域;虽然调用了free,但B引用的内存区域也不会立马被破坏,暂时还会保持着以前的值,知道在某个地方执行了malloc,随着当前内存区域被重新分配,内容才开始被破坏,这样的问题调试就比较困难了