VS 2010 CRT ::operator new源码分析

从我们写代码说起

C++面试常见的一个问题是:new与malloc的区别,然后你去网上搜索这个问题,常见回答如下:

malloc : 只能单独给对象申请空间,不能进行构造函数的调用

new :  不仅能申请动态空间,还能调用构造函数进行对成员变量初始化

我们此篇文章对哪一块源码分析呢?对,就是对new的分配内存的那部分源码进行分析。::operator new这是一个重载,重载的功能就只是分配内存,而调用构造函数在分配后的内存进行初始化,那是编译器的工作。而既有分配内存,又能在内存处初始化的功能叫做new operator。

源码分析

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
        {       // try to allocate size bytes
        void *p;
        while ((p = malloc(size)) == 0)
                if (_callnewh(size) == 0)
                {       // report no memory
                static const std::bad_alloc nomem;
                _RAISE(nomem);
                }

        return (p);
        }

上面这个就是operator new,主要就是调用一个malloc若调用不成功,调用_callnewh

extern "C" int __cdecl _callnewh(size_t size)
{
        {
            _PNH pnh = (_PNH) DecodePointer(_pnhHeap);

            if ( (pnh == NULL) || ((*pnh)(size) == 0) )
                return 0;
        }
        return 1;
}

_pnhHeap是我们在_set_new_handler函数里面EncodePointer进来的。然后调用一下我们自己的new失败处理函数。回到mallocmalloc函数里又调用了_nh_malloc_dbg

extern "C" void * __cdecl _nh_malloc_dbg (
        size_t nSize,
        int nhFlag,
        int nBlockUse,
        const char * szFileName,
        int nLine
        )
{
        int errno_tmp = 0;
        void * pvBlk = _nh_malloc_dbg_impl(nSize, nhFlag, nBlockUse, szFileName, nLine, &errno_tmp);

        if ( pvBlk == NULL && errno_tmp != 0 && _errno())
        {
            errno = errno_tmp; // recall, #define errno *_errno()
        }
        return pvBlk;
}
extern "C" static void * __cdecl _nh_malloc_dbg_impl (
        size_t nSize,
        int nhFlag,
        int nBlockUse,
        const char * szFileName,
        int nLine,
        int * errno_tmp
        )
{
        void * pvBlk;

        for (;;)
        {
            /* do the allocation
             */
            pvBlk = _heap_alloc_dbg_impl(nSize, nBlockUse, szFileName, nLine, errno_tmp);

            if (pvBlk)
            {
                return pvBlk;
            }
            if (nhFlag == 0)
            {
                if (errno_tmp)
                {
                    *errno_tmp = ENOMEM;
                }
                return pvBlk;
            }

            /* call installed new handler */
            if (!_callnewh(nSize))
            {
                if (errno_tmp)
                {
                    *errno_tmp = ENOMEM;
                }
                return NULL;
            }

            /* new handler was successful -- try to allocate again */
        }
}
/***
*void * _heap_alloc_dbg_impl() - does actual allocation
*
*Purpose:
*       Does heap allocation.
*
*       Allocates any type of supported memory block.
*
*Entry:
*       size_t          nSize       - size of block requested
*       int             nBlockUse   - block type
*       char *          szFileName  - file name
*       int             nLine       - line number
*       int  *          errno_tmp   - pointer of the temporary errno
*
*Exit:
*       Success:  Pointer to (user portion of) memory block
*       Failure:  NULL
*
*Exceptions:
*
*******************************************************************************/

extern "C" static void * __cdecl _heap_alloc_dbg_impl(
        size_t nSize,
        int nBlockUse,
        const char * szFileName,
        int nLine,
        int * errno_tmp
        )
{
        long lRequest;
        size_t blockSize;
        int fIgnore = FALSE;
        _CrtMemBlockHeader * pHead;
        void *retval=NULL;

        /* lock the heap
         */
        _mlock(_HEAP_LOCK);//这里有必要么?因为HeapAlloc本身就是互斥有序的
        __try {
            /* verify heap before allocation */
            if (check_frequency > 0)
                if (check_counter == (check_frequency - 1))
                {
                    _ASSERTE(_CrtCheckMemory());
                    check_counter = 0;
                }
                else
                    check_counter++;

            lRequest = _lRequestCurr;//调用次函数次数,全局的

            /* break into debugger at specific memory allocation */
            if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc)
                _CrtDbgBreak();

            /* forced failure */
			//_pfnAllocHook=_CrtDefaultAllocHook;默认Alloc钩子
            if ((_pfnAllocHook) && !(*_pfnAllocHook)
(_HOOK_ALLOC, NULL, nSize, nBlockUse, lRequest, (const unsigned char *)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");
            }
            else
            {
                /* 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 - nNoMansLandSize - sizeof(_CrtMemBlockHeader)))
                {
                    _RPT1(_CRT_ERROR, "Invalid allocation size: %Iu bytes.\n", nSize);
                    if (errno_tmp)
                    {
                        *errno_tmp = ENOMEM;
                    }
                }
                else
                {
                    if (!_BLOCK_TYPE_IS_VALID(nBlockUse))
                    {
                        _RPT0(_CRT_ERROR, "Error: memory allocation: bad memory block type.\n");
                    }

                    blockSize = sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize;

                    RTCCALLBACK(_RTC_FuncCheckSet_hook,(0));
                    pHead = (_CrtMemBlockHeader *)_heap_alloc_base(blockSize);

                    if (pHead == NULL)
                    {
                        if (errno_tmp)
                        {
                            *errno_tmp = ENOMEM;
                        }
                        RTCCALLBACK(_RTC_FuncCheckSet_hook,(1));
                    }
                    else
                    {

                        /* commit allocation */
                        ++_lRequestCurr;

                        if (fIgnore)//如果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 */
                            if (SIZE_MAX - _lTotalAlloc > nSize)
                            {
                                _lTotalAlloc += nSize;//_lTotalAlloc与_lCurAlloc区别是啥?
                            }
                            else
                            {
                                _lTotalAlloc = SIZE_MAX;
                            }
                            _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);//0xFD
                        memset((void *)(pbData(pHead) + nSize), _bNoMansLandFill, nNoMansLandSize);

                        /* fill data with silly value (but non-zero) */数据初始化为0xCD
                        memset((void *)pbData(pHead), _bCleanLandFill, nSize);

                        RTCCALLBACK(_RTC_FuncCheckSet_hook,(1));
 					  //返回我们可以使用的内存地址
                        retval=(void *)pbData(pHead);
                    }
                }
            }

        }
        __finally {
            /* unlock the heap
             */
            _munlock(_HEAP_LOCK);
        }

        return retval;
}
typedef struct _CrtMemBlockHeader
{
        struct _CrtMemBlockHeader * pBlockHeaderNext;
        struct _CrtMemBlockHeader * pBlockHeaderPrev;
        char *                      szFileName;//调用此函数的文件名称
        int                         nLine;//调用此函数行号
#ifdef _WIN64
        /* These items are reversed on Win64 to eliminate gaps in the struct
         * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
         * maintained in the debug heap.
         */
        int                         nBlockUse;
        size_t                      nDataSize;
#else  /* _WIN64 */
        size_t                      nDataSize;//请求分配的大小
        int                         nBlockUse;//用途,比如_NORMAL_BLOCK
#endif  /* _WIN64 */
        long                        lRequest;//	请求号(第多少次调用次函数)
        unsigned char               gap[nNoMansLandSize];
        /* followed by:
         *  unsigned char           data[nDataSize];
         *  unsigned char           anotherGap[nNoMansLandSize];
         */
} _CrtMemBlockHeader;

上面的这个结构就是为了把分配的内存挂到链表中去。

static _CrtMemBlockHeader * _pFirstBlock;
static _CrtMemBlockHeader * _pLastBlock;

遍历CRT堆

下面这段代码可以遍历一个程序中CRT分配的内存:

#define _CRTBLD
#include "D:\SoftWare\vs2010\VC\crt\src\dbgint.h"
int _tmain(int argc, _TCHAR* argv[])
{
	int* p = new int(11);

	_CrtMemBlockHeader* pHead = (_CrtMemBlockHeader*)((char*)p-sizeof(_CrtMemBlockHeader));
	while(pHead!=NULL)
	{
		static size_t index = 0;
		printf("%d:%s:%d\n",index++,pHead->szFileName,pHead->nLine);
		pHead = pHead->pBlockHeaderNext;
	}
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值