最初是在知乎上看到这个问题的C++ delete[] 是如何知道数组大小的?,我也挺好奇,所以就作了一番工作。

申请内存时,指针所指向区块的大小这一信息,其实就记录在该指针的周围
看下面这段代码:

 #include<cstdio>
#include<iostream>
#include<malloc.h>
#include<assert.h>
#include<ctime>
using namespace std; #define size 16 int main(void)
{
void * p = NULL;
srand(time());
int a = ;
while (a--)
{
int n = rand() % ;
p = malloc(n);
size_t w = *((size_t*)((char*)p - size));
cout << "w=" << w << endl;
cout << "n=" << n << endl;
assert(w == n);
free(p);
}
return ;
}

(注:如果是X86的CPU,请将 size 改为 8)

你会发现 w 和 n 始终是一致的,,这样其实不是巧合,来看 M$ 编译器 \ vc \include\ 目录下 malloc.h这一头文件 中 184 到 209 行的代码:

 //这儿是根据不同的硬件平台的宏定义
#if defined (_M_IX86)
#define _ALLOCA_S_MARKER_SIZE 8
#elif defined (_M_X64)
#define _ALLOCA_S_MARKER_SIZE 16
#elif defined (_M_ARM)
#define _ALLOCA_S_MARKER_SIZE 8
#elif !defined (RC_INVOKED)
#error Unsupported target platform.
#endif /* !defined (RC_INVOKED) */ _STATIC_ASSERT(sizeof(unsigned int) <= _ALLOCA_S_MARKER_SIZE); #if !defined (__midl) && !defined (RC_INVOKED)
#pragma warning(push)
#pragma warning(disable:6540)
__inline void *_MarkAllocaS(_Out_opt_ __crt_typefix(unsigned int*) void *_Ptr, unsigned int _Marker)
{
if (_Ptr)
{
*((unsigned int*)_Ptr) = _Marker;
//
_Ptr = (char*)_Ptr + _ALLOCA_S_MARKER_SIZE;
//最后返回给调用者的指针,是原始指针偏移了_ALLOCA_S_MARKER_SIZE的新指针,这也是刚才我将指针向后偏移,就能得到该指针所指向内存区块的大小的原因。
}
return _Ptr;
}

再来看看在 M$ 编译器中它是如何释放的,同样在 mallloc.h 文件249行到274行:

 /* _freea must be in the header so that its allocator matches _malloca */
#if !defined (__midl) && !defined (RC_INVOKED)
#if !(defined (_DEBUG) && defined (_CRTDBG_MAP_ALLOC))
#undef _freea
__pragma(warning(push))
__pragma(warning(disable: ))
_CRTNOALIAS __inline void __CRTDECL _freea(_Pre_maybenull_ _Post_invalid_ void * _Memory)
{
unsigned int _Marker;
if (_Memory)
{
_Memory = (char*)_Memory - _ALLOCA_S_MARKER_SIZE;
//获得原始指针
_Marker = *(unsigned int *)_Memory;//得到指针所指区块的大小
if (_Marker == _ALLOCA_S_HEAP_MARKER)
{
free(_Memory);
}
#if defined (_ASSERTE)
else if (_Marker != _ALLOCA_S_STACK_MARKER)
{
#pragma warning(suppress: 4548) /* expression before comma has no effect */
_ASSERTE(("Corrupted pointer passed to _freea", ));
}
#endif /* defined (_ASSERTE) */
}
}

再来看看 SGI STL标准库源码 stl_alloc.h 文件209 行到 246行 debug_alloc类模板的设计:

 // Allocator adaptor to check size arguments for debugging.
// Reports errors using assert. Checking can be disabled with
// NDEBUG, but it's far better to just use the underlying allocator
// instead when no checking is desired.
// There is some evidence that this can confuse Purify.
template <class _Alloc>
class debug_alloc { private: enum {_S_extra = }; // Size of space used to store size. Note
// that this must be large enough to preserve
// alignment. //这儿就像它所说的那样
public: static void* allocate(size_t __n)
{
//
这里实际申请的内存大小要多 个字节
char* __result = (char*)_Alloc::allocate(__n + (int) _S_extra);
*(size_t*)__result = __n;//前 4 个字节用于存储区块大小,可以看到,它预留了4个字节的空白区,具体原由 还望大牛能指出,==。
return __result + (int) _S_extra;//最后返回相对于原始指针偏移8个字节的新指针
} static void deallocate(void* __p, size_t __n)
{
char* __real_p = (char*)__p - (int) _S_extra;//获得原始指针
assert(*(size_t*)__real_p == __n);//这里增加了一个断言,防止析构了被破坏的指针
_Alloc::deallocate(__real_p, __n + (int) _S_extra);
} static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz)
{
char* __real_p = (char*)__p - (int) _S_extra;
assert(*(size_t*)__real_p == __old_sz);
char* __result = (char*)
_Alloc::reallocate(__real_p, __old_sz + (int) _S_extra,
__new_sz + (int) _S_extra);
*(size_t*)__result = __new_sz;
return __result + (int) _S_extra;
} };

再来看看 gcc 下,其实也有类似的设计:

 #if(defined(_X86_) && !defined(__x86_64))
#define _ALLOCA_S_MARKER_SIZE 8
#elif defined(__ia64__) || defined(__x86_64)
#define _ALLOCA_S_MARKER_SIZE 16
#endif #if !defined(RC_INVOKED)
static __inline void *_MarkAllocaS(void *_Ptr,unsigned int _Marker) {
if(_Ptr) {
*((unsigned int*)_Ptr) = _Marker;
_Ptr = (char*)_Ptr + _ALLOCA_S_MARKER_SIZE;
}
return _Ptr;
}
#endif
 #ifndef RC_INVOKED
#undef _freea
static __inline void __cdecl _freea(void *_Memory) {
unsigned int _Marker;
if(_Memory) {
_Memory = (char*)_Memory - _ALLOCA_S_MARKER_SIZE;
_Marker = *(unsigned int *)_Memory;
if(_Marker==_ALLOCA_S_HEAP_MARKER) {
free(_Memory);
}
#ifdef _ASSERTE
else if(_Marker!=_ALLOCA_S_STACK_MARKER) {
_ASSERTE(("Corrupted pointer passed to _freea",));
}
#endif
}
}
#endif /* RC_INVOKED */

其实,很多在实际写代码中困惑我们的问题,都可以通过 阅读相关源代码来得到 答案。

所以,经常阅读那些开源代码,还是相当有好处的 :)

free() 是如何释放不同内存区块大小的指针?的更多相关文章

  1. 用 free 或 delete 释放了内存之后,立即将指针设置为 NULL,防止产 生“野指针”

    用 free 或 delete 释放了内存之后,立即将指针设置为 NULL,防止产 生“野指针”. #include <iostream> using namespace std; /* ...

  2. C语言中free函数是如何确定要释放多少内存空间的

    本文链接:http://www.cnblogs.com/xxNote/p/4009359.html 今天看书的时候看到free函数释放动态申请的内存时只需要把内存块的首地址传过去就行了,显然仅仅依靠首 ...

  3. 手工释放linux内存——/proc/sys/vm/drop_caches

    --手工释放linux内存——/proc/sys/vm/drop_caches 总有很多朋友对于Linux的内存管理有疑问,之前一篇日志似乎也没能清除大家的疑虑.而在新版核心中,似乎对这个问题提供了新 ...

  4. 如何计算Java对象所占内存的大小

    [ 简单总结: 随便一个java项目,引入jar包: lucene-core-4.0.0.jar 如果是 maven项目,直接用如下依赖: <dependency> <groupId ...

  5. linux如何手动释放linux内存

    当在Linux下频繁存取文件后,物理内存会很快被用光,当程序结束后,内存不会被正常释放,而是一直作为caching.这个问题,貌似有不少人在问,不过都没有看到有什么很好解决的办法.那么我来谈谈这个问题 ...

  6. Android中App可分配内存的大小

    现在真实测试结果: 1,为了搞清楚每个应用程序在Android系统中最多可分配多少内存空间,我们使用了真机进行测试,测试机型为魅族MX4 Pro,3G内存. 测试方法是直接申请一块较大的内存空间,看应 ...

  7. free()后内存不释放问题 - 内存缓冲池技术(转)

    起因 下面这段代码执行后,内存有增无减,增加了200M,iOS平台200M不能接受了 // STL 集合类 void test1() { list<int> mList; for (int ...

  8. Android中App可分配内存的大小(转)

    转自:http://blog.csdn.net/u011506413/article/details/50965435 现在真实测试结果: 1,为了搞清楚每个应用程序在Android系统中最多可分配多 ...

  9. 释放linux内存中的cache缓存

    echo 3 > /proc/sys/vm/drop_caches 记一次 经常用  exp 导出oracle全量数据库,发现linux内存一直在减小没有释放,即使 oracle重启也不行,只有 ...

随机推荐

  1. MVC 依赖注入/控制反转

    http://www.cnblogs.com/cnmaxu/archive/2010/10/12/1848735.html http://www.cnblogs.com/artech/archive/ ...

  2. 如何成为一名优秀的web前端工程师(前端攻城师)?

    程序设计之道无远弗届,御晨风而返.———— 杰佛瑞 · 詹姆士 我所遇到的前端程序员分两种:第一种一直在问:如何学习前端?第二种总说:前端很简单,就那么一点东西. 我从没有听到有人问:如何做一名优秀. ...

  3. 关键字instanceof和final

    Instanceof关键字(类似oc的isKindOfClass 和 isMemberOfClass) instanceof(实例类型) 关键字作用: 1.判断某一个对象是否属于某一个类 2.inst ...

  4. VirtualBox检查更新失败解决办法

    5.0————>5.1的更新: 1.VirtualBox5.0检查更新失败 2.官网下载最新版本 3.安装最新版本到原来旧版本的路径下 关键的地方来了:我可是担心坏了,怕安装新版本,会破坏原来已 ...

  5. 【bzoj3172】 [Tjoi2013]单词

    题目描述 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. 输入 第一个一个整数N,表示有多少个单词,接下来N行每行一个单词.每个 ...

  6. linux 通用时钟框架CCF

    linux CCF 时钟框架 简单介绍 这里讲的时钟是给soc各组件提供时钟的树状框架,并非内核使用的时间,和其它模块一样,clk也有框架,用以适配不同的平台.适配层之上是客户代码和接口,也就是各模块 ...

  7. careercup-数组和字符串1.5

    1.5 利用字符重复出现的次数,编写一个方法,实现基本的字符串压缩功能.比如,字符串”aabcccccaaa“会变成”a2b1c5a3“.若”压缩“后的字符串没有变短,则返回原先的字符串. 类似 le ...

  8. LabVIEW设计模式系列——状态机

    标准:1.状态用枚举自定义类型,便于统一管理修改.2.一般地应该有:Initialize,Idle,Stop,Blank状态.3.Initialize进行一些初始化的操作:Idle一种过渡状态,用于和 ...

  9. PureMVC(JS版)源码解析(一):观察者模式解析

          假设一种情景,在程序开发中,我们需要在某些数据变化时,其他的类做出相应,例如在游戏中,升级一件装备,会触发玩家金币数量改变,背包数据改变和冷却队列数据改变等等.我们不可能设置setInte ...

  10. 编写跨平台代码之memory alignment

    编写网络包(存储在堆上)转换程序时,在hp-ux机器上运行时会遇到 si_code: 1 - BUS_ADRALN - Invalid address alignment. Please refer ...