粗解“new”之来龙去脉(一)

来源:互联网 发布:手机telnet软件 编辑:程序博客网 时间:2024/05/27 12:22

        使用C++的程序员都很熟悉使用new来分配一块内存,但是new函数具体是怎么工作的?可能很多人都没有花时间去研究,今天花了半天时间,查阅了相关的一些源代码,对它的来龙去脉有了一个初步的了解,写得不是很详细,一个是记录一下,另一个就是抛砖引玉,有兴趣的人可以深入研究。如果有不对的地方,也欢迎大家指教。

    由于文章较长,分成四个部分。

    文中列出了讲解所用到的源代码,仅供大家参考。

    好,接下来就开始我们的“new”之旅:

    分配一段内存首先当然是调用new函数去分配,我们就从new函数出发:

void * operator new( unsigned int cb ){        return _nh_malloc( cb, 1 );}

(注:也有别的版本的new,但对后面的分析没有影响)

 

    可以看出,new只是简单地调用了_nh_malloc, 其中第一个参数cb是要分配的大小,第二个参数是有没有new handler,使用new时传入了1,表示是有new handler的,关于new handler后面还会提到,但它的作用和使用方法不在本文范围内,可以自行在网上查找。

void * __cdecl _nh_malloc (        size_t nSize,        int nhFlag        ){        return _nh_malloc_dbg(nSize, nhFlag, _NORMAL_BLOCK, NULL, 0);}

    这个函数也只是调用了它的debug版_nh_malloc_dbg。接着往下看这个函数,这个函数主要是一个无限循环的for语句块,有三种返回的情况:
1 成功分配一块内存;
2 分配失败且没有new handler设置;
3 分配失败且调用_callnewh返回0。

   从下面的代码我们也能知道,使用new去分配一块内存时,如果失败的话会调用new handler,然后接着去尝试分配内存,直到分配成功或_callnewh返回0。

void * __cdecl _nh_malloc_dbg (        size_t nSize,        int nhFlag,        int nBlockUse,        const char * szFileName,        int nLine        ){        void * pvBlk;        for (;;)        {            /* lock the heap             */            _mlock(_HEAP_LOCK);            /* do the allocation             */            pvBlk = _heap_alloc_dbg(nSize, nBlockUse, szFileName, nLine);            /* unlock the heap             */            _munlock(_HEAP_LOCK);            if (pvBlk || nhFlag == 0)                return pvBlk;            /* call installed new handler */            if (!_callnewh(nSize))                return NULL;            /* new handler was successful -- try to allocate again */        }}


_callnewh函数源代码,就是去调用new handler函数, _pnhHeap是一个函数指针。

extern "C" int __cdecl _callnewh(size_t size){        {            _PNH pnh = _pnhHeap;            if ( (pnh == NULL) || ((*pnh)(size) == 0) )                return 0;        }        return 1;}

 

可以看出分配内存的事是在函数_heap_alloc_dbg做的,我们再看看这个函数。

#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1))
 
static unsigned char _bNoMansLandFill = 0xFD; void * __cdecl _heap_alloc_dbg(        size_t nSize,        int nBlockUse,        const char * szFileName,        int nLine        ){        long lRequest;        size_t blockSize;        int fIgnore = FALSE;        _CrtMemBlockHeader * pHead;        /* verify heap before allocation */        if (_crtDbgFlag & _CRTDBG_CHECK_ALWAYS_DF)            _ASSERTE(_CrtCheckMemory());        lRequest = _lRequestCurr;        /* break into debugger at specific memory allocation */        if (lRequest == _crtBreakAlloc)            _CrtDbgBreak();        /* forced failure */        if (!(*_pfnAllocHook)(_HOOK_ALLOC, NULL, nSize, nBlockUse, lRequest, szFileName, nLine))        {            if (szFileName)                _RPT2(_CRT_WARN, "Client hook allocation failure at file %hs line %d.\n",                    szFileName, nLine);            else                _RPT0(_CRT_WARN, "Client hook allocation failure.\n");            return NULL;        }        /* cannot ignore CRT allocations */        if (_BLOCK_TYPE(nBlockUse) != _CRT_BLOCK &&            !(_crtDbgFlag & _CRTDBG_ALLOC_MEM_DF))            fIgnore = TRUE;        /* Diagnostic memory allocation from this point on */        if (nSize > (size_t)_HEAP_MAXREQ ||            nSize + nNoMansLandSize + sizeof(_CrtMemBlockHeader) > (size_t)_HEAP_MAXREQ)        {            _RPT1(_CRT_ERROR, "Invalid allocation size: %u bytes.\n", nSize);            return NULL;        }        if (!_BLOCK_TYPE_IS_VALID(nBlockUse))        {            _RPT0(_CRT_ERROR, "Error: memory allocation: bad memory block type.\n");        }        blockSize = sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize;#ifndef WINHEAP        /* round requested size */        blockSize = _ROUND2(blockSize, _GRANULARITY);#endif  /* WINHEAP */        pHead = (_CrtMemBlockHeader *)_heap_alloc_base(blockSize);        if (pHead == NULL)            return NULL;        /* commit allocation */        ++_lRequestCurr;        if (fIgnore)        {            pHead->pBlockHeaderNext = NULL;            pHead->pBlockHeaderPrev = NULL;            pHead->szFileName = NULL;            pHead->nLine = IGNORE_LINE;            pHead->nDataSize = nSize;            pHead->nBlockUse = _IGNORE_BLOCK;            pHead->lRequest = IGNORE_REQ;        }        else {            /* keep track of total amount of memory allocated */            _lTotalAlloc += nSize;            _lCurAlloc += nSize;            if (_lCurAlloc > _lMaxAlloc)                _lMaxAlloc = _lCurAlloc;            if (_pFirstBlock)                _pFirstBlock->pBlockHeaderPrev = pHead;            else                _pLastBlock = pHead;            pHead->pBlockHeaderNext = _pFirstBlock;            pHead->pBlockHeaderPrev = NULL;            pHead->szFileName = (char *)szFileName;            pHead->nLine = nLine;            pHead->nDataSize = nSize;            pHead->nBlockUse = nBlockUse;            pHead->lRequest = lRequest;            /* link blocks together */            _pFirstBlock = pHead;        }        /* fill in gap before and after real block */        memset((void *)pHead->gap, _bNoMansLandFill, nNoMansLandSize);        memset((void *)(pbData(pHead) + nSize), _bNoMansLandFill, nNoMansLandSize);        /* fill data with silly value (but non-zero) */        memset((void *)pbData(pHead), _bCleanLandFill, nSize);        return (void *)pbData(pHead);}

      抛开其它的一些辅助设置和操作,我们主要是关心在什么地方分配内存,返回的指针是pHead, 而pHead是调用了函数 _heap_alloc_base得到地址,然后设置一些参数,设置几位为FD,设置nsize个0,返回这个地址。可见是在_heap_alloc_base做的分配内存的事。如果有兴趣可以new一块内存,看看内存中的值为多少?

 

原创粉丝点击