现在的位置: 首页 > 综合 > 正文

_BLOCK_TYPE_VALID(pHead->nBlockUse),_CrtIsValidHeapPointer(pUserData),动态链接库内存分配

2013年03月08日 ⁄ 综合 ⁄ 共 2694字 ⁄ 字号 评论关闭

环境描述:vc6.0,工程和动态链接库都使用了stlport,在动态链接库外定义了一个空的string变量,以引用的形式传入动态链接库,在动态链接库内给这个string变量赋值(这个字符串要比较长),出错。

 

问题分析:在动态链接库外string变量定义时分配了内存,因为这个值为空所以分配的空间较小,在动态链接库内要被赋一个较大的值,所以要给string重新分配内存,因为动态链接库内外分配内存的上下文环境不同,所以产生错误。

 

解决方法:在调用动态链接库工程以及动态链接库工程的Project Settings->C/C++,Category中选择Preprocessor,查看Preprocessor definitions:查看是否有_STLP_USE_NEWALLOC的定义,要保持一致,即要么两个工程都要有,要么两个都没有。

 

 

延伸:造成这个问题的原因是动态链接库内和动态链接库外分配内存的环境不一样。以前我们在没有引入stlport的情况下使用Cstring作为参数在动态链接库内外传递,经常会出现这样的问题,那时网上大多数人的建议是在传递的时候改用指针。后来我们改用了strlport,可以使用宏定义来控制内存分配的环境。对于其他的情况,当然是谁分配谁释放。

 

 

---------------------------------------

 

下面是《window核心编程》19.1 DLL与进程的地址空间

中的描述

 

 

创建D L L常常比创建应用程序更容易,因为D
L L往往包含一组应用程序可以使用的自主函数。在D L L中通常没有用来处理消息循环或创建窗口的支持代码。D L
L只是一组源代码模块,每个模块包含了应用程序(可执行文件)或另一个D L
L将要调用的一组函数。当所有源代码文件编译后,它们就像应用程序的可执行文件那样被链接程序所链接。但是,对于一个D L L来说,你必须设定该连链程序的/ D L
L开关。这个开关使得链接程序能够向产生的D L L文件映像发出稍有不同的信息,这样,操作系统加载程序就能将该文件映像视为一个D L
L而不是应用程序。

在应用程序(或另一个D L L)能够调用D L
L中的函数之前,D L
L文件映像必须被映射到调用进程的地址空间中。若要进行这项操作,可以使用两种方法中的一种,即加载时的隐含链接或运行期的显式链接。隐含链接将在本章的后面部分介绍,显式链接将在第2
0章中介绍。

一旦D L
L的文件映像被映射到调用进程的地址空间中, D L L的函数就可以供进程中运行的所有线程使用。实际上, D L L几乎将失去它作为D L
L的全部特征。对于进程中的线程来说,D L L的代码和数据看上去就像恰巧是在进程的地址空间中的额外代码和数据一样。当一个线程调用D L L函数时,该D L
L函数要查看线程的堆栈,以便检索它传递的参数,并将线程的堆栈用于它需要的任何局部变量。此外, D L L中函数的代码创建的任何对象均由调用线程所拥有,而D L
L本身从来不拥有任何东西。

例如,如果Vi r t u a l A l l
o c函数被D L L中的一个函数调用,那么将从调用线程的进程地址空间中保留一个地址空间的区域,该地址空间区域将始终处于保留状态,因为系统并不跟踪D L
L中的函数保留该区域的情况。保留区域由进程所拥有,只有在线程调用Vi r t u a l F r e e函数或者进程终止运行时才被释放。

如你所知,可执行文件的全局变量和静态变量不能被同一个可执行文件的多个运行实例共享。Windows
98能够确保这一点,方法是在可执行文件被映射到进程的地址空间时为可执行文件的全局变量和静态变量分配相应的存储器。Windows
2000确保这一点的方法是使用第1 3章介绍的写入时拷贝(c o p y - o n - w r i t e)机制。D L
L中的全局变量和静态变量的处理方法是完全相同的。当一个进程将D L
L的映像文件映射到它的地址空间中去时,系统将同时创建全局数据变量和静态数据变量的实例。

注意必须注意的是,单个地址空间是由一个可执行模块和若干个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函数的调用就会失败。我经常看到编程人员编写这样的代码,结果都失败了。

有一个很方便的方法可以解决这个问题。当一个模块提供一个用于分配内存块的函数时,该模块也必须提供释放内存的函数。
让我们将上面的代码改写成下面的样子:

 

VOID EXEFunc()
{
PVOID pv = DLLFunc();
//Access the storage pointed to by pv...
//Makes no assumptions about C/C++ run-time heap
DLLFreeFunc(pv);
}
PVOID DLLFunc()
{
//Allocate block from DLL's C/C++ run-time heap
PVOID pv = malloc(100);
return(pv);
}

BOOL DLLFreeFunc(PVOID pv)
{
//Free block from DLL's C/C++ run-time heap
return(free(pv));
}

这个代码是正确的,它始终都能正确地运行。当你编写一个模块时,不要忘记其他模块中的函数也许没有使用C / C + +来编写,因此可能无法使用m a
l l o c和f r e e函数进行内存的分配。应该注意不要在代码中使用这些假设条件。另外,在内部调用m a l l o c和f r e
e函数时,这个原则对于C + +的n e w和d e l e t e操作符也是适用的。

 

【上篇】
【下篇】

抱歉!评论已关闭.