您的位置:首页 > 服装鞋帽 > 内衣 > malloc 与 free

malloc 与 free

luyued 发布于 2011-01-13 02:01   浏览 N 次  

malloc/free
malloc/free是C和C++语言的标准库函数, 可以被覆盖, 需要头文件库函数支持,
不在编译器控制范围之内, 不能够执行构造函数和析构函数的任务

malloc标准库函数的作用:分配动态堆内存
free标准库函数的作用:释放动态堆内存

用malloc分配的一块内存, 你要用指针访问并且要通过移动指针来访问内存数据

malloc只认字节数, 不管数据类型, 只能返回一个void*, 程序员要把void*强制类型转换成相应的指针类型,
同时还要用 if (NULL != ...) 来判断申请内存空间是否成功


new/delete
new/delete是C++的操作符, 是保留字, 可以被重载, 不需要头文件支持,
在编译器控制范围之内, 能够执行构造函数和析构函数的任务

new操作符的作用:分配动态堆内存、初始化对象(调用构造函数)
delete操作符的作用:清理对象(调用析构函数)、释放动态堆内存

用new创建的对象, 你可以用成员函数访问, 不用直接访问它的地址空间。

new可以认为是malloc加构造函数的执行, new出来的指针是直接带类型信息的, 不用进行强制类型转换

malloc/free 和 new/delete 的联系与区别
对于基本数据类型来说, 由于不像“对象”那样有构造与析构的过程,所以对它们而言,
malloc/free 和 new/delete是等价的

既然new/delete的功能完全覆盖了malloc/free, 为什么C++不把malloc/free淘汰出局呢?
因为C++程序经常要调用C函数, 而C程序只能用malloc/free分配和释放动态堆内存

内存泄漏对于malloc或者new都可以检查出来的, 区别在于new可以指明是那个文件的那一行, 而malloc没有这些信息

如果用free释放“new创建的动态对象”, 那么该对象因无法执行析构函数而可能导致程序出错。
如果用delete释放“malloc申请的动态内存”, 理论上讲程序不会出错, 但是该程序的可读性很差。
所以new/delete必须配对使用, malloc/free也一样。

Visual C++ 6.0中的malloc/free 和 new/delete

int* p = (int*)malloc(5 * sizeof(int));

void* __cdecl malloc(size_t nSize)
{
void* res = _nh_malloc_dbg(nSize, _newmode, _NORMAL_BLOCK, NULL, 0);

return res;
}

void* __cdecl _nh_malloc_dbg(size_t nSize, int nhFlag, int nBlockUse, const char * szFileName, int nLine)
{
void* pvBlk;

for (;;)
{
_mlock(_HEAP_LOCK);

__try
{
pvBlk = _heap_alloc_dbg(nSize, nBlockUse, szFileName, nLine);
}
__finally
{
_munlock(_HEAP_LOCK);
}

if (pvBlk || nhFlag == 0)
return pvBlk;
}
}

void* __cdecl _heap_alloc_dbg(size_t nSize, int nBlockUse, const char * szFileName, int nLine)
{
pHead = (_CrtMemBlockHeader*)_heap_alloc_base(blockSize);

// 在你分配的内存后面加4个256(即: 4个FD), 在释放内存时, 会以这4个FD作为内存界限的标志
// _bNoMansLandFill的值是253, nNoMansLandSize的值是4
memset((void*)pHead->gap, _bNoMansLandFill, nNoMansLandSize);
memset((void*)(pbData(pHead) + nSize), _bNoMansLandFill, nNoMansLandSize);
memset((void*)pbData(pHead), _bCleanLandFill, nSize);

return (void*)pbData(pHead);
}

void* __cdecl _heap_alloc_base(size_t size)
{
return HeapAlloc(_crtheap, 0, size); // HeapAlloc函数是微软内部API, 用于分配堆内存
}

free(p);

void __cdecl free(void * pUserData)
{
_free_dbg(pUserData, _NORMAL_BLOCK);
}

void __cdecl _free_dbg(void* pUserData, int nBlockUse)
{
_ASSERTE(_CrtIsValidHeapPointer(pUserData));

// _bNoMansLandFill的值是253, nNoMansLandSize的值是4
CheckBytes(pHead->gap, _bNoMansLandFill, nNoMansLandSize);
CheckBytes(pbData(pHead) + pHead->nDataSize, _bNoMansLandFill, nNoMansLandSize);

memset(pHead, _bDeadLandFill, sizeof(_CrtMemBlockHeader) + pHead->nDataSize + nNoMansLandSize);
_free_base(pHead);
}

int __cdecl _CrtIsValidHeapPointer(const void* pUserData)
{
return HeapValidate(_crtheap, 0, pHdr(pUserData)); // HeapValidate函数是微软内部API, 用于验证指定堆的有效性
}

static int __cdecl CheckBytes(unsigned char* pb, unsigned char bCheck, size_t nSize) // bCheck的值是253, nSize的值是4
{
int bOkay = TRUE;

while (nSize--)
{
if (*pb++ != bCheck) // 检查你分配的内存后面是否有4个256(即: 4个FD), 如果没有, 就说明有错误
bOkay = FALSE;
}

return bOkay; // 检查无误, 返回TRUE
}

void __cdecl _free_base(void* pBlock)
{
PHEADER pHeader;

if (pBlock == NULL)
return;

HeapFree(_crtheap, 0, pBlock); // 释放一个内存块, 这个内存块是用HeapAlloc或HeapReAlloc分配的
}

int* a = new int[6];

void* operator new(unsigned int cb)
{
void* res = _nh_malloc(cb, 1);

return res;
}

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

delete a;

void operator delete(void* pUserData)
{
_CrtMemBlockHeader * pHead;

if (pUserData == NULL)
return;

_mlock(_HEAP_LOCK);

pHead = pHdr(pUserData);

_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));

_free_dbg(pUserData, pHead->nBlockUse);

_munlock(_HEAP_LOCK);
}

class Person
{
public:
Person() { a = 1; b = 2.0; c = 'f'; }
int GetA() { return a; }
double GetB() { return b; }
char GetC() { return c; }
private:
int a;
double b;
char c;
};

int main()
{
Person* p = new Person();

delete p;

return 0;
}

Person* p = new Person();这句代码先调用
void* operator new(unsigned int cb)
{
void* res = _nh_malloc(cb, 1);

return res;
}
后调用Person() { a = 1; b = 2.0; c = 'f'; }

delete p;这句代码调用
void operator delete(void* pUserData)
{
_CrtMemBlockHeader * pHead;

if (pUserData == NULL)
return;

_mlock(_HEAP_LOCK);

pHead = pHdr(pUserData);

_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));

_free_dbg(pUserData, pHead->nBlockUse);

_munlock(_HEAP_LOCK);
}

int main()
{
char* p = new char;

cin >> p;
cout << p;
delete p;

return 0;
}

Debug调试程序:
char* p = new char; 这句代码使p的内容变成0x00382DE0
00382DE0 CD FD FD FD FD
分析: CD是帮char占一个字节的位置, 4个FD作为内存界限的标志

cin >> p; 这句代码调用 istream& istream::operator>>(char* s)
如果你输入a[Enter], 则内存如下:
00382DE0 61 00 FD FD FD
分析: Ox61是字符'a'的ASCII码, 0x00是operator>>加的'\0', 因为char* s是字符串, 要以'\0'结尾, p是实参, s是形参

cout << p; 这句代码调用 ostream& ostream::operator<<(const char* s)
执行完这句代码后, 屏幕并没有输出任何信息

delete p; 这句代码调用 void operator delete(void* pUserData)
在void __cdecl _free_dbg(void* pUserData, int nBlockUse)的第二个CheckBytes代码里面出现问题Debug Error,
选择“忽略”错误, 最终屏幕还是没有输出任何信息


Debug直接执行程序:
如果你输入a[Enter], 则会出现问题Debug Error


Release直接执行程序:
如果你输入a[Enter], 则会很好地输出a
如果你输入abcdefghijk[Enter], 则会很好地输出abcdefghijk

图文资讯
广告赞助商