为什么不要在DLL模块内分配内存而在调用模块释放?

来源:互联网 发布:linux输入中文乱码 编辑:程序博客网 时间:2024/05/17 20:30

        很多初学编程的朋友都会碰到这样的问题:有时需要在一个Dll中获取一个字符串或者一个数组并返回给调用程序。

        在这个问题中,内存分配成了一个费脑筋的问题。如果在调用程序中分配内存,不知道该分配多大。如果在Dll内部分配内存,则Dll无法释放。于是很多朋友都尝试过第三种解决方案:在Dll中分配内存,然后在调用程序中释放。不幸的是,通常会得到这个错误:

Dll:
void GetString(char** ppStr, int& len){len = 100;char* pStr = new char[len];//do something fill pStr//for examplepStr[0] = 'a';pStr[1] = 'b';pStr[2] = 'c';pStr[3] = '\0';*ppStr = pStr;}
Main:
HMODULE hModule = LoadLibrary(Dll路径);if(hModule){GetString pFuncGetStr = (GetString) GetProcAddress(hModule, "GetString");if(pFuncGetStr){char** ppStr = new char*();int len = 0;pFuncGetStr(ppStr, len);printf("%s", *ppStr);printf("%d", strlen(*ppStr));delete [] *ppStr;delete ppStr;ppStr = NULL;}}
在 delete [] *ppStr; 时候会发生这样错误:

        而奇怪的是,如果将Dll 中的函数复制粘贴到调用程序中,使用同样的调用方式就没有问题。为什么?

        看一下MSDN上对_CrtisValidHeapPointer的解释:Verifies that a specified pointer is in the local heap (debug version only). 主要意思是,_CrtisValidHeapPointer是用来验证一个指针指向的内存是否是分配在本地堆中的。每一个C运行库的实例都维护着自己的本地堆,在一个C运行库中分配的堆内存,在另一个C运行库的实例中使用_CrtisValidHeapPointer验证就会返回FALSE。默认情况下,VC编译器(VC6.0)创建的Dll工程使用静态链接(Setting--C++选项卡--CodeGeneration) Debug MultiThreaded(Debug版本),Console的工程也使用静态链接,程序运行的时候,是存在两个C运行库的实例的。在Dll中的C运行库分配的内存在Main函数中释放的时候,会使用_CrtisValidHeapPointer检查这个内存(Debug版本),这时候返回的当然是FALSE,于是断言失败,显示出上边的失败提示。

        为了验证这个结论,我们把C运行库的 链接 方式都改为动态链接(Debug MultiThread Dll),重新编译后再次运行就没有问题了。

        明白了这一点以后,这个问题的解决方案也就有了:第一种解决方案是,Dll分配Dll释放。给DllGetString配备一个DllFreeString。第二种解决方案是,调用者分配,调用者释放。那么如何确定分配内存大小呢?当然是也是询问Dll得到。参考Windows的很多API,可以这样定义:    int GetString(char** ppStr, int& len);如果调用成功返回值等于len,如果返回值大于len,说明内存分配的不够大,需要重新分配。使用的时候需要两次调用,先用一个试探值得到所需内存,再分配内存,例子如下:

char** ppStr = new char*();    int len = 0;    int nret = GetString(ppStr, len);    if(nret > len)    {        len = nret;        *ppStr = new char[len];        nret = GetString(ppStr, len);        if(nret == len)        {            printf("%s", *ppStr);        }                delete [] *ppStr;        delete ppStr;        ppStr = NULL;    }

        第三种解决方案就是像上边那样将Dll和主程序的C运行库都该为动态连接,但是这只是不得已的一种补救做法,一般情况下还是应该使用正确的设计。

        事实上,即使只在同一个模块中调用,多数情况下也应该遵循谁分配谁释放的原则。这样的代码易于阅读和维护。谁知道哪一天哪一个函数可能会被移动到一个独立的模块中呢?

参考:
http://msdn.microsoft.com/zh-cn/library/ys6cfhhh.aspx
http://bbs.csdn.net/topics/300108371
http://hi.baidu.com/xtwgigu/item/5a5e51ceab9cbf3298b498f2






原创粉丝点击