再看malloc动态分配内存

一向对内存敏感性不好,或者说不太有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,随着当前内存区域被重新分配,内容才开始被破坏,这样的问题调试就比较困难了

发表回复