从我们写代码说起
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失败处理函数。回到malloc。malloc函数里又调用了_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;
}