new/delete 额外的内存信息

最近偶然看到一篇文章“谁动了我的指针”,里面提到了关于new/delete在内存分配中保存的额外信息,一时兴起就查了下这方面的资料,在这个过程中也确实学到了不少东西。


1. delete如何知道删除的内存大小?

这个问题,很多程序员都问过自己,包括我。其实基本原理并不复杂,只是与实际开发关系不大的问题总是容易被我们放到一边,以至学习编程几年了都没去找过这个答案。其实基本原理很简单,作为一个程序员,如果让我们自己去实现这个功能,第一反应就是new的时候在某个地方把内存的大小保存下来,那么如何实现?通常就有两种方法:

1)在实际分配的内存前后额外分配一些空间把这些信息保存下来。

2)全局的保留一份“指针地址-内存块大小”的map表用于查找。


MS的VC采用的就是第一种方式,GCC是否也是如此未做验证。特别的,在vc的debug版本中还引入了内存链的形式,其节点结构如下:

typedef struct _CrtMemBlockHeader
{
        struct _CrtMemBlockHeader * pBlockHeaderNext;            // 下一个节点指针
        struct _CrtMemBlockHeader * pBlockHeaderPrev;            // 前一个节点指针
        char *                      szFileName;                  // 调用new的文件名
        int                         nLine;                       // 调用new的行号
        size_t                      nDataSize;                   // 调用new分配内存大小
        int                         nBlockUse;                   // 本块内存使用目的
        long                        lRequest;                    // 请求编号
        unsigned char               gap[nNoMansLandSize];        // 内存前面的空白
        /* followed by:
         *  unsigned char           data[nDataSize];             // 真正的内存
         *  unsigned char           anotherGap[nNoMansLandSize]; // 内存后面的空白
         */
} _CrtMemBlockHeader;

为了验证,可以写个简单的程序验证下:

int main(int argc, char* argv[])
{
	int *test_point = new int;
	return 0;
}

 下面是内存信息:




其中文件名和行号均为0,因为只有在使用DEBUG_NEW时这两个值才会有,该宏在afx.h中定义,可在程序前面包含该头文件,增加#define new DEBUG_NEW即可看到这两个信息。在VC中,实际可用内存的前后分别用四个字节作为定界符,其值均为fdfdfdfd,这样的数据我们通常称之为Magic Number,即魔数字,下面是MS VC中的一些Magic Number,便于理解:

* 0xABABABAB : Used by Microsoft's HeapAlloc() to mark "no man's land" guard bytes after allocated heap memory* 0xABADCAFE : A startup to this value to initialize all free memory to catch errant pointers* 0xBAADF00D : Used by Microsoft's LocalAlloc(LMEM_FIXED) to mark uninitialised allocated heap memory* 0xBADCAB1E : Error Code returned to the Microsoft eVC debugger when connection is severed to the debugger* 0xBEEFCACE : Used by Microsoft .NET as a magic number in resource files* 0xCCCCCCCC : Used by Microsoft's C++ debugging runtime library to mark uninitialised stack memory* 0xCDCDCDCD : Used by Microsoft's C++ debugging runtime library to mark uninitialised heap memory* 0xDEADDEAD : A Microsoft Windows STOP Error code used when the user manually initiates the crash* 0xFDFDFDFD : Used by Microsoft's C++ debugging heap to mark "no man's land" guard bytes before and after allocated heap memory

* 0xFEEEFEEE : Used by Microsoft's HeapFree() to mark freed heap memory需要注意的是以上仅限于MS VC,GCC采用的标识符甚至是方式都可能不同,有兴趣的话也可以去研究一下。魔数字的作用主要是用于:

1)校验指针的有效性;

2)判断内存操作是否越界;

2. new/delete和malloc/free C语言提供的内存分配函数是malloc/free,而C++则提供了new/delete,是否是多余的呢?亦或是它们究竟有何不同。原因在于,C++中引入了“类”的概念。malloc/free关心的仅仅是内存的大小,而不关心内存内存储的是什么;而new/delete关心的是对象,需要考虑构造与析构。也就是说,new/delete在malloc/free的内存管理的基础上增加了调用构造/析构函数的功能,这也就是为什么new/delete和malloc/free不能混用的原因。严格的讲存在构造与析构的类型,都不应该用malloc/free,除非你自己做过重载。例如free一个class,它会正确释放class占用的内存,却不会调用析构函数——这意味着在该class并不会出现内存泄漏,但如果你本意是在析构函数中释放一些的东西的话,就可能在别的地方造成内存泄漏或别的问题。3. delete和delete [ ]通常我们的原则是:直接new的用delete,数组方式new的用delete []。但你是否想过为什么呢?设想如果让我们自己实现的话,为了正确释放对象数组,除了它占用的空间,我们还需要知道什么?是数组元素的个数,因为对于对象,除了释放空间,我们还需要调用构造/析构函数,不知道分配的个数肯定是不行的。结合第一、二节提到的内容,分析下面的程序对应的内存:

struct TEST
{
	int a;
	~TEST(){};
};

int main(int argc, char* argv[])
{
	TEST *test_point = new TEST[5];
	return 0;
}

下面是对应的内存信息



 

这里面我们可以看到一个奇怪的现象:并没有把个数信息写到使用内存的前面(即fdfdfdfd的前面),而是在分配时多分配了4字节,同时返回的指针进一步向后偏移了4字节。原因可能是在这种方式下,前面提到的链表节点结构体同样试用(不需要增加个数的字段),而不需要额外再定义新的结构体。

此外,只有像上面的程序那样显示的声明析构函数(在父类中也算),内存中才会出现该字段,估计是因为默认的构造/析构函数啥都不做,是否有这个字段都无所谓了,就跟一般类型int/char等,即便”int *p = new int[4];delete p;也不会出错“,当然,并不推荐这么写。

总结,其实这边提到的东西跟软件开发并没有太大的关系,但有助于对一些概念的理解。同时,在某些情况可能会对程序的调试有帮助。需要注意的是,这些东西都属于编译器的私有实现,实际的开发中并不应该故意的去使用其中的一些信息,例如魔数字,否则写出来的程序的可移植性将极差!简单的说,C++标准仅仅规定了new/delete应该分配给我们指定大小的内存,并返回该内存块的首址,具体采用什么方式实现,每个编译其都可能不同。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值