C语言动态内存分配:(一)malloc/free的实现及malloc实际分配/释放的内存
最新个人博客 shankusu.me
以下内容转载或参考或引用自
https://blog.csdn.net/zxx910509/article/details/62881131
一、malloc/free概述
malloc是在C语言中用于在程序运行时在堆中进行动态内存分配的库函数。free是进行内存释放的库函数。
1、函数原型
- #include <stdlib.h>
- void *malloc(
- size_t size
- );
- void free(
- void* memblock
- );
2、返回值
成功时,返回所分配存储空间的起始地址;返回值类型为void*,在C语言中可以把void*直接付给具体的类型,但是在C++中必须进行强制类型转换
失败时(内存不足时)返回NULL
size为0时,返回的指针不是NULL;但是除了free,别的地方不要使用这个指针。
3、使用示例
- #include <stdlib.h> /* For _MAX_PATH definition */
- #include <stdio.h>
- #include <malloc.h>
- void main( void )
- {
- char *string;
- /* Allocate space for a path name */
- string = malloc( _MAX_PATH );
- // In a C++ file, explicitly cast malloc's return. For example,
- // string = (char *)malloc( _MAX_PATH );
- if( string == NULL )
- printf( "Insufficient memory available\n" );
- else
- {
- printf( "Memory space allocated for path name\n" );
- free( string );
- printf( "Memory freed\n" );
- }
- }
二、malloc实际分配的内存大小
malloc实际分配的内存会大于我们需要的size。主要由两方面因素决定:
1、字节对齐。会对齐到机器最受限的类型(具体的实现因机器而异)。
2、“块头部信息”。每个空闲块都有“头部”控制信息,其中包含一个指向链表中下一个块的指针、当前块的大小和一个指向本身的指针。为了简化块对齐,所有块的大小都必须是头部大小的整数倍,且头部已正确对齐。
在VC平台下由_CrtMemBlockHeader结构体实现。
以下为《C程序设计语言》中给出的通过union进行的头部实现,其中假定机器的受限类型为long。
- typedef long Align;/*按照long类型的边界对齐*/
- union header/*块的头部*/
- {
- struct
- {
- union header *ptr;/*空闲块链表中的下一块*/
- unsigned size;/*本块的大小*/
- }s;
- Align x;/*强制块对齐*/
- };
说明:
(1)实际分配的内存块将多一个单元,用于头部本身。实际分配的块的大小被记录在头部的size字段中。
(2)size字段是必须的,因为malloc函数控制的块不一定是连续的,这样就不能通过指针算术运算计算其大小。
(3)malloc返回的是空闲块的首地址,而不是首地址。
三、malloc/free实现过程
1、空闲存储空间以空闲链表的方式组织(地址递增),每个块包含一个长度、一个指向下一块的指针以及一个指向自身存储空间的指针。( 因为程序中的某些地方可能不通过malloc调用申请,因此malloc管理的空间不一定连续。)
2、当有申请请求时,malloc会扫描空闲链表,直到找到一个足够大的块为止(首次适应)(因此每次调用malloc时并不是花费了完全相同的时间)。
3、如果该块恰好与请求的大小相符,则将其从链表中移走并返回给用户。如果该块太大,则将其分为两部分,尾部的部分分给用户,剩下的部分留在空闲链表中(更改头部信息)。因此malloc分配的是一块连续的内存。
4、释放时,首先搜索空闲链表,找到可以插入被释放块的合适位置。如果与被释放块相邻的任一边是一个空闲块,则将这两个块合为一个更大的块,以减少内存碎片。
四、实现
以下为《C语言程序设计语言》中给出的一种实现方法
1、malloc的实现
- typedef union header Header;
- static Header base;/*从空链表开始*/
- static Header *freep = NULL;/*空闲链表的初始指针*/
- void *malloc(unsigned nbytes)
- {
- Header *p, *prevp;
- Header *morecore(unsigned);
- unsigned nunits;
- nunits = (nbytes+sizeof(Header)-1)/sizeof(Header) + 1;
- if((prevp = freep) == NULL) /* 没有空闲链表 */
- {
- base.s.ptr = freep = prevp = &base;
- base.s.size = 0;
- }
- for(p = prevp->s.ptr; ;prevp = p, p= p->s.ptr)
- {
- if(p->s.size >= nunits) /* 足够大 */
- {
- if (p->s.size == nunits) /* 正好 */
- prevp->s.ptr = p->s.ptr;
- else /*分配末尾部分*/
- {
- p->s.size -= nunits;
- p += p->s.size;
- p->s.size = nunits;
- }
- freep = prevp;
- return (void*)(p+1);
- }
- if (p== freep) /* 闭环的空闲链表*/
- if ((p = morecore(nunits)) == NULL)
- return NULL; /* 没有剩余的存储空间 */
- }
- }
说明:
(1)malloc实际分配的空间是Header大小的整数倍,并且多出一个Header空间用于放置Header
(2)式nunits = (nbytes+sizeof(Header)-1)/sizeof(Header) + 1中的减1是为了防止(nbytes+sizeof(Header))%sizeof(Header) == 0时,多分配了一个Header大小的空间
(3)第一次调用malloc函数时,freep为NULL,系统将创建一个退化的空闲链表,它只包含一个大小为0的块,且该块指向自己。任何时候,当请求空闲空间时,都将搜索空闲块链表。搜索从上一次找到空闲块的地方(freep)开始。该策略可以保证链表是均匀的。如果找到的块太大,则将其尾部返回给用户,这样,初始块的头部只需要修改size字段即可。
(4)任何时候,返回给用户的指针都指向块内的空闲存储空间,即比指向头部的指针大一个单元。
(5)sbrk不是系统调用,是C库函数。sbrk/brk是从堆中分配空间,本质是移动一个位置,向后移就是分配空间,向前移就是释放空间,sbrk用相对的整数值确定位置,如果这个整数是正数,会从当前位置向后移若干字节,如果为负数就向前若干字节。在任何情况下,返回值永远是移动之前的位置。在LINUX中sbrk(0)能返回比较精确的虚拟内存使用情况。链接:sbrk()/brk()--改变数据长度
(6)链接:brk()分配过程
2、morecore的实现
函数morecore用来向操作系统请求存储空间,其实现细节因系统的不同而不同。因为向系统请求存储空间是一个开销很大的操作,因此我们不希望每次调用malloc时都执行该操作,基于这个考虑,morecore函数请求至少NALLOC个单元。这个较大的块将根据需要分成较小的块。在设置完size字段后,morecore函数调用free函数把多余的存储空间插入到空闲区域中。
- #define NALLOC 1024 /* 最小申请单元数 */
- static Header *morecore(unsigned nu)
- {
- char *cp;
- Header *up;
- if(nu < NALLOC)
- nu = NALLOC;
- cp = sbrk(nu * sizeof(Header));
- if(cp == (char *)-1) /* 没有空间*/
- return NULL;
- up = (Header *)cp;
- up->s.size = nu;
- free((void *)(up+1));
- return freep;
- }
说明:
(1)没有存储空间时sbrk调用返回-1.因此需要将-1强制类型转换为char*类型,以便 与返回值进行比较。而且,强制类型转换使得函数不会受不同机器中指针表示的不同的影响。
3、free的实现
free函数从freep指向的地址开始,逐个扫描空闲链表,寻找可以插入空闲块的地方。该位置可能在链表的末尾或者两个空闲块之间。在任何一种情况下,如果被释放的块与另一空闲块相邻,则将这两个块合并。
- void free(void *ap)
- {
- Header *bp,*p;
- bp = (Header *)ap -1; /* 指向块头 */
- for(p=freep;!(bp>p && bp< p->s.ptr);p=p->s.ptr)
- if(p>=p->s.ptr && (bp>p || bp<p->s.ptr))
- break; /* 被释放的块在链表的开头或结尾*/
- if (bp+bp->s.size==p->s.ptr) /*与上一相邻块合并 */
- {
- bp->s.size += p->s.ptr->s.size;
- bp->s.ptr = p->s.ptr->s.ptr;
- }
- else
- bp->s.ptr = p->s.ptr;
- if (p+p->s.size == bp)/* 与下一相邻块合并 */
- {
- p->s.size += bp->s.size;
- p->s.ptr = bp->s.ptr;
- }
- else
- p->s.ptr = bp;
- freep = p;
- }
五、注意事项
链接:malloc/free使用注意事项
参考文献:
1、《C程序设计语言》
2、《C和指针》
3、《C和C++安全编码》
4、链接:如何实现一个malloc
C语言动态内存分配:(一)malloc/free的实现及malloc实际分配/释放的内存相关推荐
- linux 在指定区域分配内存 c语言,C语言动态内存分配:(一)malloc/free的实现及malloc实际分配/释放的内存...
一.malloc/free概述 malloc是在C语言中用于在程序运行时在堆中进行动态内存分配的库函数.free是进行内存释放的库函数. 1.函数原型 #include void *malloc( s ...
- c语言malloc引用类型作参数,C语言动态内存函数的理解和总结
第一:内存的使用 内存可以分为以下三个主要的部分:栈区.堆区.静态区 栈区(stack):存放的是局部变量.函数的形参等都是在该区上存放的. 堆区(heap):动态内存函数开辟的空间.比如malloc ...
- c语言分配多一个字符空间,关于C语言动态给字符串分配内存空间问题
在动态分配的空间中如何输入字符串,关于C语言动态给字符串分配内存空间的问题相信很多朋友都不太了解,下面维维带来相关解答,赶紧看看吧. 用malloc来分配内存空间. 即输入几个字节的字符 系统就自动帮 ...
- c语言动态内存分配数组,【C】动态内存分配
## 动态内存分配的意义 C语言中的一切操作都是基于内存的 变量和数组都是内存的别名 内存分配由编译器在编译期间决定 定义数组的时候必须指定数组长度 数组长度是在编译期就必须确定的需求: 程序在运行过 ...
- C语言动态内存开辟详解(malloc,calloc,realloc,free,柔型数组)
目录 一.概述 二.相关库函数的使用 1.malloc 2.calloc malloc vs. calloc 异同 3.free的使用 4.realloc 三.易错点 四.C\C++程序的内存开辟规则 ...
- C语言动态内存管理和动态内存分配函数
给变量分配内存空间可分为静态内存分配和动态内存分配. 静态内存分配属于编译时给变量分配的空间,动态分配属于在程序运行时给变量分配的空间 静态分配属于栈分配,动态分配属于堆分配 运行效率上,静态内存比动 ...
- C语言-动态内存管理(malloc()、calloc()、realloc()、free())
C语言 动态内存分配 文章目录 C语言 动态内存分配 前言 一.为什么存在动态内存分配? 二.动态内存函数的介绍 1.初识malloc()和free() 2.malloc()和free()的简单使用 ...
- C语言动态内存分配详解
文章目录 前言 一.为什么存在动态内存分配 1.已掌握的内存开辟方式 2.上述开辟空间方式的特点 3.为什么存在动态内存分配 二.动态内存函数的介绍 1.malloc 2.free 3.calloc ...
- 浅谈C语言动态内存分配及柔性数组
文章目录 前言 1.动态内存的简单介绍 1.动态内存分配是什么? 2.为什么存在动态内存分配? 3.动态内存分配具体方法 1.动态内存函数 2.动态内存注意事项 2.经典面试题分析 3.C/C++程序 ...
最新文章
- Visual Paradigm 教程[UML]:如何在SoaML中建模多方服务?
- ssh_exchange_identification: Connection closed by remote host 问题的解决 (转)
- 田志刚:所有大公司都是小公司(二)
- cool pyramid diagram
- 盘点大厂的那些开源项目 - 华为
- 【iVX 初级工程师培训教程 10篇文拿证】05 画布及飞机大战游戏制作
- 15什么时候你最想打人
- 魔鬼作坊第一部实践----第九课
- 八皇后问题初始思路python_【单人解谜】经典的八皇后问题解析
- 不用再为机翻头疼!sci论文写作翻译神器推荐
- 高斯模型MATLAB的实现,高斯混合模型GMM实现matlab
- 解决微信公众号accessToken白名单问题
- 【NPM】Building fresh packages运行很久都没反应
- 保护眼睛的颜色和各种背景颜色设置方法(转)
- 分享假如你买到缩水U盘了怎么办?认倒霉?肯定不能的!
- mysql创建联合主键
- Unity读取项目文件夹图片,PC端
- 仿滴滴打车时间选择器开始结束时间
- MATLAB小知识(三)——输出矩阵到TXT
- 笔记:《深入浅出统计学》第八、九章:概率密度、正态分布(高斯分布)