在VC里面,用release模式编译运行程序的时候,堆分配(Heap allocation)的时候调用的是malloc,如果你要分配10byte的空间,那么就会只分配10byte空间,而用debug模式的时候,堆分配调用的是_malloc_dbg,如果你只要分配10byte的空间,那么它会分配出除了你要的10byte之外,还要多出约36byte空间,用于存储一些薄记信息,debug堆分配出来之后就会按顺序连成一个链。
那么我们再来看看薄记信息中有些什么。还是上面10byte分配空间的例子,那么分配出的10byte空间的前面会有一个32byte的附加信息,存储的是一个_CrtMemBlockHeader结构,可以在DBGINT.H中找到该结构的定义:
typedef struct _CrtMemBlockHeader
{
// Pointer to the block allocated just before this one:
struct _CrtMemBlockHeader *pBlockHeaderNext;
// Pointer to the block allocated just after this one:
struct _CrtMemBlockHeader *pBlockHeaderPrev;
char *szFileName; // File name
int nLine; // Line number
size_t nDataSize; // Size of user block
int nBlockUse; // Type of block
long lRequest; // Allocation number
// Buffer just before (lower than) the user's memory:
unsigned char gap[nNoMansLandSize];
} _CrtMemBlockHeader;
/* In an actual memory block in the debug heap,
* this structure is followed by:
* unsigned char data[nDataSize];
* unsigned char anotherGap[nNoMansLandSize];
*/
结构中的_CrtMemBlockHeader结构两个指针就不用解释是干嘛的了,szFileName是存储的发起分配操作的那行代码所在的文件的路径和名称,而nLine则是行号。nDataSize是请求分配的大小,我们的例子里当然就是10了,nBlockUse是类型,而lRequest 是请求号。最后一项gap,又称NoMansLand,是4byte(nNoMansLandSize=4)大小的一段区域,注意看最后几行注释就明白了,在这个结构后面跟的是用户真正需要的10byte数据区域,而其后还跟了一个4byte的Gap,那么也就是说用户申请分配的区域是被一个头结构,和一个4byte的gap包起来的。在释放这10byte空间的时候,会检查这些信息。Gap被分配之后会被以0xFD填充。检查中如果gap中的值变化了,就会以Assert fail的方式报错。不过vc6中提示的比较难懂,DAMAGE :after Normal block(#dd) at 0xhhhhhhhh,而vs2005里面会提示Heap Corruption Detected!而如果你是release版本,那么这个错误就会潜伏直到它的破坏力发生作用。也许其后的区域存储着一个除数,而你的heap corruption把它改写成了0,那么会怎么样呢? :P
至于其他的C/C++编译器中是否会有这样的机制,我就不是很清楚了,或许知道的朋友可以给我做些补充。
我的出错程序:
这是个自动转换WCHAR* 与 char*的类。
#pragma once
class CAutoString
{
public:
CAutoString();
CAutoString( const WCHAR * pszString);
CAutoString( const char * pszString);
virtual ~CAutoString();
public:
operator char *();
operator WCHAR *();
protected:
WCHAR *m_pszwString;
char *m_pszString;
};
CAutoString::CAutoString( const WCHAR *pszString)
{
int nLength = wcslen(pszString);
m_pszwString = new WCHAR[nLength]; //这个地方内存空间分配少了,导致后面写入时越界
wcscpy(m_pszwString ,pszString);
m_pszString = NULL;
}
CAutoString::CAutoString( const char *pszString)
{
int nLength = strlen(pszString) + 1 ;
m_pszString = new char[nLength];
strncpy(m_pszString ,pszString ,nLength);
m_pszwString = NULL;
}
CAutoString::~CAutoString()
{
if (m_pszwString)
delete [] m_pszwString;
if(m_pszString)
delete [] m_pszString;
}
CAutoString::operator char * () {
int nMlen;
nMlen = WideCharToMultiByte(CP_ACP, 0, m_pszwString, -1, NULL , 0, NULL, NULL);
m_pszString = new char[nMlen];
WideCharToMultiByte(CP_ACP, 0, m_pszwString, -1, m_pszString, nMlen, NULL, NULL) ;
return m_pszString;
}
CAutoString::operator WCHAR* () {
int nwLen = MultiByteToWideChar(CP_ACP, 0, m_pszString, -1, NULL, 0);
m_pszwString = new WCHAR[nwLen];
MultiByteToWideChar(CP_ACP, 0, m_pszString, -1, m_pszwString, nwLen);
return m_pszwString;
}