free()函数的原理:操作系统和编译器是如何处理free(P)函数的;释放的是什么?

z341223318 2008-02-20 09:53:41
操作系统和编译器是如何处理free(P)函数的;
释放的是什么?
如下图所示
____
p---->| 20 |0x1000
|----|
| 30 |
|----|
| 40 |
|----|
| 50 |
------
假设前面用malloc开辟了一段空间,
P指向刚才开辟的那段内存空间(0X1000).
当执行free(p);
我知道P是不指向刚才开辟的那段内存了.
可是到底释放的是什么?
整个过程和最终结果是什么?
有以下几个疑问
1,P的值还是原来的0x1000吗?
2,释放之后,原来开辟的那段空间中的20,30,40,50还存在吗?

困惑好几天了,请高手帮忙!!
...全文
1589 34 打赏 收藏 转发到动态 举报
写回复
用AI写文章
34 条回复
切换为时间正序
请发表友善的回复…
发表回复
yuanzhuohang 2010-02-12
  • 打赏
  • 举报
回复
哇哈哈……我正在学习
我看你有戏 2009-02-11
  • 打赏
  • 举报
回复

// ReadMem.cpp : Defines the entry point for the console application.
//

#include "stdio.h"
#include "windows.h"

int main(int argc, char* argv[])
{
/*
BOOL ReadProcessMemory(
HANDLE hProcess, // 被读取进程的句柄;
LPCVOID lpBaseAddress, // 读的起始地址;
LPVOID lpBuffer, // 存放读取数据缓冲区;
DWORD nSize, // 一次读取的字节数;
LPDWORD lpNumberOfBytesRead // 实际读取的字节数;
);
*/

char *pBuff = new char[50];
strcpy(pBuff,"这是一个测试字符串");
delete pBuff;

BYTE byBuff[50];
DWORD dwLen;
HANDLE hHandle = GetModuleHandle(NULL);//获取本进程句柄
ReadProcessMemory(hHandle,(void*)pBuff,byBuff,50,&dwLen);

//printf("%s,%ld",szBuff,dwLen);
for(int i=0;i<50;i++)
{
printf("%d,",byBuff[i]);
}
return 0;
}

//VC6.0下跑一下看看,ReadProcessMemory这个是根据内存地址读取数据的API函数
我看你有戏 2009-02-11
  • 打赏
  • 举报
回复

free(p);
是一个函数,是个啥函数呢

就是你写的应用程序,跟操作系统交互的函数

告诉系统,这块内存我要还给你了,不属于我了

至于操作系统怎么去安置这块内存,我们不需要去管他了

操作系统也可以看成是一个程序,也是程序员写的
deerwin1986 2009-02-10
  • 打赏
  • 举报
回复
free 是在堆上执行的
一般在malloc的时候 如果是数组 会在前一个字节记录申请的内存的大小...(win32是这样的)
在编译期间 会保留类型信息
从而在释放的时候可以知道系统的堆指针应该回退多少...
dongpy 2009-02-10
  • 打赏
  • 举报
回复
[Quote=引用楼主 z341223318 的帖子:]
假设前面用malloc开辟了一段空间,
P指向刚才开辟的那段内存空间(0X1000).
当执行free(p);
我知道P是不指向刚才开辟的那段内存了.
可是到底释放的是什么?
整个过程和最终结果是什么?
有以下几个疑问
1,P的值还是原来的0x1000吗?
2,释放之后,原来开辟的那段空间中的20,30,40,50还存在吗?
[/Quote]
首先p的值不变,还是存储刚才的地址。再说void free(void*)也不可能改变实参p的值。

而p指向的空间,则有几种可能:
若free后,C库立即将物理空间归还给内核(即通过brk断开了一些页面的映射),那么
1:访问*p会出现段错误(访问越界)。
这种情况不常出现,因为只有p指向堆顶部且空间较大(至少超过1个页面),才可能发生。
所以一般情况下,free(p)只是改变了C库中堆管理器的内部数据结构(如将p指向的空间加入空闲链表中),那么空间中的20,30,40,50还是存在的。
但是,之后如果执行了q=malloc之类的操作,那么
2:p指向的空间被分配q,对*q的写操作就会改变*p。
3:p指向的空间被用作堆管理器内部数据结构。
以上3种情况都会改变"原来开辟的那段空间中"的内容。
其中情况1对*p读写访问都会出错。
情况2,3读*p都是OK的。
而对*p写访问:
情况2会产生不可预期的错误(可能出现运行错误而退出,也可能不退出但产生错误的结果)。
情况3会出现运行错误。
dongpy 2009-02-10
  • 打赏
  • 举报
回复
[Quote=引用楼主 z341223318 的帖子:]
操作系统和编译器是如何处理free(P)函数的;
[/Quote]
堆内存由操作系统和C库共同管理:应用程序--(free)--C库--(brk/sbrk)--内核。
yiallen 2009-02-04
  • 打赏
  • 举报
回复
赞代码
r_swordsman 2008-02-23
  • 打赏
  • 举报
回复
例子:


#include "stdafx.h"
#include <malloc.h>



int main()
{
char* p;
p=(char*)malloc(100);
p[0] = 16;
free(p); // free 之后 p 值不变,但内容已经改变,具体内容未知
p[0] = 12; // 违规内存访问,应用程序退出是将产生错误,提示堆被破坏或违规的内存访问。但一般不影响程序的运行结果
}




对于lz的问题

1,P的值还是原来的0x1000吗?
2,释放之后,原来开辟的那段空间中的20,30,40,50还存在吗?

回答:

1,P的值不变;
2,原来开辟的那段空间中的20,30,40,50一般不存在,其值是不确定的。
r_swordsman 2008-02-23
  • 打赏
  • 举报
回复

/***
*void _free_dbg() - free a block in the debug heap
*
*Purpose:
* Frees any type of supported block.
*
*Entry:
* void * pUserData - pointer to a (user portion) of memory block in the
* debug heap
* int nBlockUse - block type
*
*Return:
* <void>
*
*******************************************************************************/


extern "C" _CRTIMP void __cdecl _free_dbg(
void * pUserData,
int nBlockUse
)
{
/* lock the heap
*/
_mlock(_HEAP_LOCK);

__try {
/* allocate the block
*/
_free_dbg_nolock(pUserData, nBlockUse);
}
__finally {
/* unlock the heap
*/
_munlock(_HEAP_LOCK);
}
}

extern "C" void __cdecl _free_dbg_nolock(


void * pUserData,
int nBlockUse
)
{
_CrtMemBlockHeader * pHead;

RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));

/* verify heap before freeing */

if (check_frequency > 0)
if (check_counter == (check_frequency - 1))
{
_ASSERTE(_CrtCheckMemory());
check_counter = 0;
}
else
check_counter++;

if (pUserData == NULL)
return;

/* check if the heap was not allocated by _aligned routines */
if ( nBlockUse == _NORMAL_BLOCK)
{
if ( CheckBytes((unsigned char*)((uintptr_t)pUserData & ~(sizeof(uintptr_t) -1)) -nAlignGapSize,_bAlignLandFill, nAlignGapSize))
{
_RPT1(_CRT_ERROR, "The Block at 0x%p was allocated by aligned routines, use _aligned_free()", pUserData);
errno = EINVAL;
return;
}
}

/* forced failure */
if ((_pfnAllocHook) && !(*_pfnAllocHook)(_HOOK_FREE, pUserData, 0, nBlockUse, 0L, NULL, 0))
{
_RPT0(_CRT_WARN, "Client hook free failure.\n");

return;
}

/*
* If this ASSERT fails, a bad pointer has been passed in. It may be
* totally bogus, or it may have been allocated from another heap.
* The pointer MUST come from the 'local' heap.
*/
_ASSERTE(_CrtIsValidHeapPointer(pUserData));

/* get a pointer to memory block header */
pHead = pHdr(pUserData);

/* verify block type */
_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));

/* if we didn't already check entire heap, at least check this object */
if (!(_crtDbgFlag & _CRTDBG_CHECK_ALWAYS_DF))
{
/* check no-mans-land gaps */
if (!CheckBytes(pHead->gap, _bNoMansLandFill, nNoMansLandSize))
_RPT3(_CRT_ERROR, "HEAP CORRUPTION DETECTED: before %hs block (#%d) at 0x%p.\n"
"CRT detected that the application wrote to memory before start of heap buffer.\n",
szBlockUseName[_BLOCK_TYPE(pHead->nBlockUse)],
pHead->lRequest,
(BYTE *) pbData(pHead));

if (!CheckBytes(pbData(pHead) + pHead->nDataSize, _bNoMansLandFill, nNoMansLandSize))
_RPT3(_CRT_ERROR, "HEAP CORRUPTION DETECTED: after %hs block (#%d) at 0x%p.\n"
"CRT detected that the application wrote to memory after end of heap buffer.\n",
szBlockUseName[_BLOCK_TYPE(pHead->nBlockUse)],
pHead->lRequest,
(BYTE *) pbData(pHead));
}

RTCCALLBACK(_RTC_FuncCheckSet_hook,(0));

if (pHead->nBlockUse == _IGNORE_BLOCK)
{
_ASSERTE(pHead->nLine == IGNORE_LINE && pHead->lRequest == IGNORE_REQ);
/* fill the entire block including header with dead-land-fill */
memset(pHead, _bDeadLandFill,
sizeof(_CrtMemBlockHeader) + pHead->nDataSize + nNoMansLandSize);
_free_base(pHead);
RTCCALLBACK(_RTC_FuncCheckSet_hook,(1));
return;
}

/* CRT blocks can be freed as NORMAL blocks */
if (pHead->nBlockUse == _CRT_BLOCK && nBlockUse == _NORMAL_BLOCK)
nBlockUse = _CRT_BLOCK;

/* Error if freeing incorrect memory type */
_ASSERTE(pHead->nBlockUse == nBlockUse);

/* keep track of total amount of memory allocated */
_lCurAlloc -= pHead->nDataSize;

/* optionally reclaim memory */
if (!(_crtDbgFlag & _CRTDBG_DELAY_FREE_MEM_DF))
{
/* remove from the linked list */
if (pHead->pBlockHeaderNext)
{
pHead->pBlockHeaderNext->pBlockHeaderPrev = pHead->pBlockHeaderPrev;
}
else
{
_ASSERTE(_pLastBlock == pHead);
_pLastBlock = pHead->pBlockHeaderPrev;
}

if (pHead->pBlockHeaderPrev)
{
pHead->pBlockHeaderPrev->pBlockHeaderNext = pHead->pBlockHeaderNext;
}
else
{
_ASSERTE(_pFirstBlock == pHead);
_pFirstBlock = pHead->pBlockHeaderNext;
}

/* fill the entire block including header with dead-land-fill */
memset(pHead, _bDeadLandFill,
sizeof(_CrtMemBlockHeader) + pHead->nDataSize + nNoMansLandSize);
_free_base(pHead);
}
else
{
pHead->nBlockUse = _FREE_BLOCK;

/* keep memory around as dead space */
memset(pbData(pHead), _bDeadLandFill, pHead->nDataSize);
}
RTCCALLBACK(_RTC_FuncCheckSet_hook,(1));
}

r_swordsman 2008-02-23
  • 打赏
  • 举报
回复

/***
*void * _heap_alloc_dbg() - 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
*
*Exit:
* Success: Pointer to (user portion of) memory block
* Failure: NULL
*
*Exceptions:
*
*******************************************************************************/

extern "C" 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;
void *retval=NULL;

/* lock the heap
*/
_mlock(_HEAP_LOCK);
__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 */
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);
errno = ENOMEM;
}
else
{
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 */

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

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

/* 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);

RTCCALLBACK(_RTC_FuncCheckSet_hook,(1));

retval=(void *)pbData(pHead);
}
}
}

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


r_swordsman 2008-02-23
  • 打赏
  • 举报
回复

要了解 free 释放内存的具体细节,先要了解 malloc 分配内存的具体细节。

要了解 C 的内存分配,就要知道,C 程序是从 OS 中请求内存的,以 Windows 为例,CRT 调用 WIN API
HeapAlloc 来请求堆内存分配的,HeapAlloc 需要一个句柄,指向进程的堆,这是 OS 管理内存用的,扯远

了。

好了,现在 CRT 已经有了堆内存了,那么应用程序要在堆上请求内存怎么做呢?不用我说吧?用 malloc,

而 malloc 其实调来调去,其实是调用 _heap_alloc_dbg,_heap_alloc_dbg 接受一个参数 size_t nSize

用来表示程序要分配的内存大小,以 malloc(100) 为例,此参数也就是 100,但 _heap_alloc_dbg 不是请

求分配 100 字节的内存,CRT 需要维护一个 _CrtMemBlockHeader 的链表,_CrtMemBlockHeader 有一个成

员 nDataSize 来表示用户请求分配的内存大小,_heap_alloc_dbg 实际上就是通过 HeapAlloc 来请求分配

内存的,分配大小为 sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize,即 _CrtMemBlockHeader

结构的大小(32字节)加上用户请求的大小(100),再加上 4 字节的标志。

好了,现在已经分配了足够的内存,难道就把所有的内存都给应用程序使用吗?当然不是,CRT 得到系统分

配的内存的首地址 pHead,而给应用程序的内存实际上就是 pHead 偏移 32 字节(_CrtMemBlockHeader 的

大小)所指向的地址,_heap_alloc_dbg 返回的就是这个地址。


应用程序使用完内存之后,调用 free 函数,free 函数实际上调用的是 _free_dbg 函数,再调用

_free_dbg_nolock 函数,_free_dbg_nolock 函数的第一个参数为 pUserData,即应用程序使用的内存地址

,也就是 _heap_alloc_dbg 地址,也是 malloc 返回的地址,pUserData 减去 32 字节

(_CrtMemBlockHeader 的大小)就是系统分配内存的首地址 pHead,通过 pHead 就可以得到一个

_CrtMemBlockHeader 结构,这个结构的 nDataSize 成员就是应用程序请求的内存大小,以 malloc(100) 为

例,此成员也就是 100,这样就可以计算出系统分配了多少内存,sizeof(_CrtMemBlockHeader) +

nDataSize + nNoMansLandSize,即 32 + 100 + 4,为 136,所有可以正确释放分配的内存。


下面是 _heap_alloc_dbg 和 _free_dbg_nolock 的源代码:

eqxu 2008-02-21
  • 打赏
  • 举报
回复
http://blog.csdn.net/eqxu/archive/2007/06/12/1648658.aspx

看看linux 指针参数所指向空间大小的计算 malloc realloc alloc 指针指向空间的大小的计算 malloc_chunk结构 你就理解free的工作原理了
xxmv99 2008-02-21
  • 打赏
  • 举报
回复
顶11楼~~~
iami007 2008-02-21
  • 打赏
  • 举报
回复
我用Dev-C++4.9.9.2写了个程序验证:

#include <stdio.h>

int main(){
int* p=(int*)malloc(sizeof(int));
printf("%d\n",p);
p[0]=1;
p[1]=2;
printf("%d\n",p);
free(p);
printf("%d %d %d %d\n",p,p[0],p[1],p[3]);
system("pause");
return 0;
}

输出的结果是:

4007000
4007000
4007000 0 2 524697
Press any key to continue . . .
sheenl 2008-02-20
  • 打赏
  • 举报
回复
这个很麻烦的。
首先你操作的都是虚拟内存, 而不是物理内存。 虚拟内存和物理内存的联系由操作系统维护, 用户空间的程序无能为力。

根据内存管理的算法,虚拟内存, 可能仅仅只是一种地址空间, 并没有实际的物理内存与之对应。 这种地址空间仅仅存在一个信息, 就是是否和进程有归属关系。

free, 从道理上说, 就是表明这个地址不再属于我们的进程了。 但是实际上并不是一调用free, 这个地址空间就不属于我们的进程了, 而是根据管理算法决定, 有可能会在以后的某个时间才被设置成不属于本进程, 在那之前, 你一直都可以访问那个虚拟地址。
free可以确定是是, 一旦调用后, 那个地址是不被保护的, 也就是说其他的变量随时可能占用那个地址, 所以那个地址的值是未确定的。 实际上在被最终设置成不属于本进程之前, 你可以把那个地址当成一个volatile型的变量
zzyjsjcom 2008-02-20
  • 打赏
  • 举报
回复
1,P的值还是原来的0x1000吗?
是, 除非free后, p = NULL;
2,释放之后,原来开辟的那段空间中的20,30,40,50还存在吗?
可能存在


free 之前, 别人不能使用0x1000的地址空间, 但free后, 别的程序可以用0x1000的地址空间


cnzdgs 2008-02-20
  • 打赏
  • 举报
回复
内存管理一般由操作系统来实现,内存管理的方式有很多,各种操作系统也有所不同,同一操作系统中不同类型的内存其管理方式也不同。目前常见的管理方式主要有两种。一种是在分配内存的前面再多分配一点空间,用以记录内存块的各种信息,Free时参照这些信息来处理;另一种是构造一张表,该表可以是各种形态,表中记录所分配的各个内存块的信息。

C语言的函数调用是值传递,所以Free不会改变p的值。
执行Free后,内存就可以被再次分配给其它程序使用了,所以内存中的数据无法预料。
执行Free后,原则上不能再使用p来访问内存了。
lala_benben 2008-02-20
  • 打赏
  • 举报
回复
在分配内存时 在指针指向的内存前,有一段存放了这次分配内存的大小。。。free的时候就根据指针指向的内存地址来找到这段内存的长度, 然后把他释放掉
xgbing 2008-02-20
  • 打赏
  • 举报
回复
malloc之后,这块内存被系统记录“已使用”
free之后,这块内存被系统记录“可用”,当它还没有被用作其它用途之前它的内容还是不变的
xgbing 2008-02-20
  • 打赏
  • 举报
回复
3.但是不能用P再去引用那段内存了.
----------
没有其它程序使用的话重新分配内存时可能在同一块
加载更多回复(14)

70,033

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧