2010年9月13日星期一

转:谁分配谁释放

原文链接:http://chengwu2003.spaces.live.com/blog/cns!FC65A74BE5AB6D03!227.entry

在《Windows核心编程》第19章有一段话,

必须注意的是,单个地址空间是由一个可执行模块和若干个D L L模块组成的。这些模块中,有些可以链接到静态版本的C / C + +运行期库,有些可以链接到一个D L L版本的C / C + +运行期库,而有些模块(如果不是用C / C + +编写的话)则根本不需要C / C + +运行期库。许多开发人员经常会犯一个常见的错误,因为他们忘记了若干个C / C + +运行期库可以存在于单个地址空间中。请看下面的代码:

VOID EXEFunc() {
    PVOID pv = DLLFunc();
    // Access the storage pointed to by pv...
    // Assumes that pv is in EXE's C/C++ run-time heap
    free(pv);
}

PVOID DLLFunc() {
    // Allocate block from DLL's C/C++ run-time heap
    return(malloc(100));  
}

那么你是怎么看待这个问题的呢?上面这个代码能够正确运行吗? D L L函数分配的内存块是由E X E的函数释放的吗?答案是可能的。上面显示的代码并没有为你提供足够的信息。如果E X E和D L L都链接到D L L的C / C + +运行期库,那么上面的代码将能够很好地运行。但是,如果两个模块中的一个或者两个都链接到静态C / C + +运行期库,那么对free函数的调用就会失败。我经常看到编程人员编写这样的代码,结果都失败了。

为什么包含这段代码的EXE和DLL不能静态链接,而且任何一个模块都不能静态链接?原因如下:

在VC的C运行库中有一个全局句柄_crtheap,malloc和free都是在这个句柄上使用HeapAlloc和HeapFree来分配释放内存的。如果所有模块都使用动态链接,那么进程空间中只载入一个C运行库,也就只存在一个_crtheap。反过来,只要有一个模块静态链接了C运行库,那么运行库中的全局变量将会被复制一份到该模块中,这样就存在两个_crtheap,进程初始化的时候就会调用HeapCreate创建两个不同的堆,很明显,在DLL的堆上分配的内存拿到EXE中用HeapFree是释放不了的,将会引发内存访问异常。所以,谁分配就应该谁释放,因为我们不是任何时刻都很清楚库函数的资源分配规则。