malloc等分配内存函数探究

相关内存分配函数:malloc, calloc,free,realloc,brk

常识普及:

一个由C/C++编译的程序占用的内存(RAM)分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另
一块区域。 - 程序结束后由系统释放。
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。

 

malloc:

原型: void *malloc(unsigned int num_bytes);

头文件:#include <stdlib.h>

功能:分配长度为num_bytes字节的内存块

说明:如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。

与new区别:在使用上,malloc 和 new 至少有两个不同: new 返回指定类型的指针,并且可以自动计算所需要大小。

 

molloc工作机制:

malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到连接表上。调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块。如果无法获得符合要求的内存块,malloc函数会返回NULL指针,因此在调用malloc动态申请内存块时,一定要进行返回值的判断。

 

特例:

	
char*ptr;
if((ptr=(char*)malloc(0))==NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");

答案:got a valid pointer

理由:

首先:
在标准的malloc实现中,并不检查输入值的大小,而是将输入值做对齐操作后直接从堆上分配空间。
其次:
不论输入值的大小为多少,在malloc的内部最小的内存分配大小是一个定值(一般是8B),因为malloc需要用这部分空间来维护堆上的内存块链表。所以当用户申请一块0B的空间时,malloc实际分配的空间是8B,如果用户申请的空间是X,则malloc实际分配的空间是(对齐(X) + 8)。这也是为什么malloc分配的空间千万不能越界使用的原因:堆的内部链表结构将被破坏。

另外对于new和delete malloc和free这样的内存分配与释放函数:到底delete和free是怎么知道要释放掉多少内存的呢?
其实在new和malloc内存分配成功时,系统出除了返回一个指向这块内存的指针外,还会获得一块用于记录此处分配的内存大小的内存块:

 

malloc分配的空间千万不能越界使用?然 越界了又如何?


1、弊端:看malloc实现机制----见上边。可知,越界后,当某种情况满足时,还会将越界的一部分作为空闲,分配给合适的malloc请求,之前申请的内存中的越界部分内容,会被更改。。

2、char *name = (char *)malloc(sizeof(char));,只得到1个字节的空间,而strcpy(name,"1234567890")则要求name至少应有11个字节空间长度,如果,你一定要这样复制,有时是可行的,因为当11 - 1 = 10个字节是空闲时,似乎没有问题,但如果不是呢,问题就来了,谁知道覆盖了哪个变量的存储单元呢。一旦覆盖,你知道你的代码的运行会如何诡异吗?---很多不可确定的情况发生!

3、因为C/C++语言并不对数组越界进行检查。而malloc通常是堆上分配内存。而堆上常常会有富裕的空间,所以写在这些地方不会出现系统异常。你可以试试存入更多的数,或分配更少的内存。这时候可能出现写入异常,但这实际上是由系统产生的。不检查越界不等于程序可以越界,出现越界以后会出现无法预测的后果。所以使用指针是要注意自己避免内存越界。

 

_msize(): 

参数 :相关指针

取得malloc()分配的空间大小
★注意:该函数为Windows独有,UNIX没有对应的函数★

 

 

_alloca():

alloca也是用来分配存储空间的,它和malloc的区别是它是在当前函数的栈上分配存储空间,而不是在堆中。
其优点是:当函数返回时,自动释放它所使用的栈。

 

calloc函数

函数原型:void *calloc(size_t n, size_t size);

功 能: 在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。

与malloc的区别:

calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据。

详见:百科

 

free:

原型: void free(void *ptr)

功 能: 释放ptr指向的存储空间。被释放的空间通常被送入可用存储区池,以后可在调用malloc、realloc以及calloc函数来再分配。

 

realloc:

realloc原型是extern void *realloc(void *mem_address, unsigned int newsize);

功能:

先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。

 

brk和sbrk

brk和sbrk主要的工作是实现虚拟内存到内存的映射.在GNUC中,内存分配是这样的:
每个进程可访问的虚拟内存空间为4G,但在程序编译时,不可能也没必要为程序分配这么大的空间,只分配并不大的数据段空间,程序中动态分配的空间就是从这一块分配的。如果这块空间不够,malloc函数族(realloc,calloc等)就调用sbrk函数将数据段的下界移动,sbrk函数在内核的管理下将虚拟地址空间映射到内存,供malloc函数使用。(参见linux内核情景分析)
所以,用户程序一般不会使用这些系统调用,除非编写自己的内存管理函数或者编写类似编译器、连接器之类的程序。

关于brk和sbrk参见: brk()和sbrk()函数的使用  

 

以上。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值