静态库,动态库,和exe之间,内存位置问题

xychzh 2011-05-29 02:03:34
helper.lib里有个类CHelper,管理1个int数据(int* pInt)
engine.dll链接了这个helper.lib,并定义了类CEngine,CEngine中定义了一个CHelper对象(CHelper m_Helper)
最后有个exe程序先是链接了helper.lib,然后又加载了engine.dll

然后:
我在exe里有个类CApp,继承自CEngine
然后在CApp里调用了CHelper的一个分配int数据的成员函数

PS:
helper.lib和engine.dll和exe用的都是"多线程调试(/MTd)"
请问:
1:这个pInt指向的内存在exe上,还是在Helper.lib上,还是在engine.dll里????
2:当CApp析构的时候,先是析构CEngine,然后析构CHelper,最后析构CApp,可是我的程序就在析构CHelper的时候崩溃了(崩溃点在delete pInt上),什么情况呢?

这个问题搞了一上午了,不得其解啊....
...全文
1216 29 打赏 收藏 转发到动态 举报
写回复
用AI写文章
29 条回复
切换为时间正序
请发表友善的回复…
发表回复
pxdbxq 2013-08-22
  • 打赏
  • 举报
回复
同意22#,基本同意24#,但是24#最后一段的说明中,我觉得“exe和dll使用相同的.lib”是不准确的,确切说它们只能使用相同的动态库。只要其中有一个使用静态库,那么必然会导致两个堆,这样释放肯定出问题。
zhangxing3134 2011-10-19
  • 打赏
  • 举报
回复
请参见《windows核心编程》第五版 19章第1小结 19.1 DLL与进程的地址空间
linsen1211 2011-05-30
  • 打赏
  • 举报
回复
路过!
董小尾 2011-05-30
  • 打赏
  • 举报
回复
很受启发……
zhang3317 2011-05-30
  • 打赏
  • 举报
回复
SonicLing 2011-05-29
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 xychzh 的回复:]
请问:我静态库里有一个CHelper的类体定义,然后在DLL里定义了CHelper的对象实例,那请问这个实例内存由helper.lib管理还是dll来管理???
[/Quote]

你的问题比较复杂,在于lib被dll和exe都使用了。lib可以理解为obj文件包,本身并不会和标准库连接。它到底和哪个版本的标准库连接,就看exe和dll用的什么库。如果exe和dll使用的标准库不同,但共用了lib的实现,那么你从代码看lib里的实现是一样的,但实施上dll和exe的实现就因为使用的库的不同而有不同了,是不兼容的。

DLL定义的CHelper实例是用DLL对应的标准库管理的。

从你的描述看,DLL里有CEngine,那个只是一个声明,这还好说。但是它使用了CHelper。CHelper在lib里使用了标准库的内存管理,那么dll里,CHelper就是和DLL的标准库连接的,即CHelper使用DLL的库来管理pInt。

然后你的EXE的CApp继承了CEngine,那么CApp也会使用CHelper来管理pInt,而这时CApp所使用的CHelper使用的是EXE的标准库。如果DLL和EXE使用相同版本的标准库,可能没问题,但不能完全保证,这依赖于库的实现,因为dll和exe的内存管理对象的实例是独立的。如果他们使用不同版本的标准库,那肯定会出问题。

所以dll和exe可以使用相同的lib,但是使用上千万不要交叉,否则你的dll和exe就是强绑定,那分离出一个dll模块有什么意义呢?exe和dll之间最好使用接口,即纯虚类,然后dll提供create和release的导出函数用来操纵这个接口。这样exe和dll的接口就不会依赖于一个具体实现了。
super_admi 2011-05-29
  • 打赏
  • 举报
回复
如果是你自己实现的CHelper,我建议你把每个释放内存语句后面加一个=NULL.然后再看看该地址指向什么时候为空。如果已经空了,还有delete,则肯定有问题。

你说的这个问题,直接原因肯定是delete重复了。
www_adintr_com 2011-05-29
  • 打赏
  • 举报
回复
纯 lib 文件链接后已经不存在了,链接的时候连接器直接把 lib 中的函数代码拷贝到对应的 exe 或 dll 中. 所以运行的时候只有 exe, dll 这些模块, 没有 lib 的东西.

内存分配最终会调用 HeapAlloc 这个 API 来分配内存,这个函数的参数需要一个堆的句柄. 这个堆句柄是在 dll 或 exe 进入 main 之前由 CRT 库来初始化的。所以每个模块都有自己独立的堆句柄,当分配和释放内存所传递的堆句柄不相同时,DEBUG 版本就会提示一个错误来提醒程序员这个问题。

下面再看静态链接和动态链接 CRT 库的区别:
静态链接时,CRT 库的内存分配代码是被拷贝到了最终执行体中(dll 或 exe), 这样每次调用 new 或 delete 时使用的是这个模块的堆句柄,如果在一个模块中分配的内存,在另外一个模块中释放就会出现问题.
动态链接时, CRT 库的代码放在自己的 dll 中, 其它使用动态链接的模块要分配或释放内存都是调用 CRT 的 dll 模块来完成的.所以所有的分配和释放都在 CRT DLL 这同一个模块中,使用的就是同一个句柄,于是就没有错误了。
xychzh 2011-05-29
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 sonicling 的回复:]
dll有自己的main,有自己的全局变量的构造和析构代码。虽然你没有直接使用这些全局变量,但是你会间接使用它们,比如malloc/free所在的C标准库,C++的new/delete也是依赖于C标准库的。

如果exe、lib和dll之间的内存管理存在交叉,这个malloc的另外一个负责free,那要看库版本和库实现了。如果版本一样,且实现不会有问题,那就没问题。否则就会出问题。很简单的道理,……
[/Quote]

请问:我静态库里有一个CHelper的类体定义,然后在DLL里定义了CHelper的对象实例,那请问这个实例内存由helper.lib管理还是dll来管理???
taodm 2011-05-29
  • 打赏
  • 举报
回复
你用事实证明了“不算”。
ljt3969636 2011-05-29
  • 打赏
  • 举报
回复
...“int数据的new和delete都在CHelper中”.那这句“CHelper的分配int数据的函数放到dll里执行”什么意思?
xychzh 2011-05-29
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 taodm 的回复:]
估计楼主还会在这个简单的内存跨界传递上折腾很久的。
内存黄金原则:谁申请谁释放。
[/Quote]

看我15楼,请问我那个不算是谁申请谁释放吗?
SonicLing 2011-05-29
  • 打赏
  • 举报
回复
dll有自己的main,有自己的全局变量的构造和析构代码。虽然你没有直接使用这些全局变量,但是你会间接使用它们,比如malloc/free所在的C标准库,C++的new/delete也是依赖于C标准库的。

如果exe、lib和dll之间的内存管理存在交叉,这个malloc的另外一个负责free,那要看库版本和库实现了。如果版本一样,且实现不会有问题,那就没问题。否则就会出问题。很简单的道理,exe和dll使用的是独立的内存管理单元,exe管理的指针到dll里面去free,或者反过来,肯定有问题。当库版本不一致时问题更突出。

所以exe和dll的对象管理要保证独立。如果两边指针没有交叉管理,就算库版本不一致也不会有什么问题。

而你的问题更复杂:你的exe、dll都连接了相同的lib,那更要保证标准库版本的一致性,因为一定存在对象的交叉问题。你的哦CHelper在exe和dll内都用了,如果他们使用的标准库版本不一致,结果可想而知了。

dll的内存会映射到所有连接的exe的进程空间中,但是dll的库的全局变量相对于exe的是独立的。dll有自己的main来初始化和析构这些全局变量。dll的全局变量在第一次被LoadLibrary的时候初始化,之后只是简单映射和增加引用计数而已。最后一次FreeLibrary的时候被析构掉。所以dll使用了exe的内存空间,但是管理机制是独立的,这就是为什么只要保证接口,那么VC6编译的exe仍然可以使用VC2010编译的dll,但这里面一定不能有对象的交叉管理。
taodm 2011-05-29
  • 打赏
  • 举报
回复
估计楼主还会在这个简单的内存跨界传递上折腾很久的。
内存黄金原则:谁申请谁释放。
xychzh 2011-05-29
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 ljt3969636 的回复:]
那几乎可以断定依然是跨模块管理内存的缘故了,原因改成MD没问题的原因那个帖子已经说了,就剩下就是为什么“CHelper的分配int数据的函数放到dll里执行,也不会崩溃”了,

你int的delete放在哪了?
[/Quote]

int数据的new和delete都在CHelper中
dll的CEngine定义了这个CHelper对象
exe继承了这个CEngine
ljt3969636 2011-05-29
  • 打赏
  • 举报
回复
那几乎可以断定依然是跨模块管理内存的缘故了,原因改成MD没问题的原因那个帖子已经说了,就剩下就是为什么“CHelper的分配int数据的函数放到dll里执行,也不会崩溃”了,

你int的delete放在哪了?
booxiong 2011-05-29
  • 打赏
  • 举报
回复
当与用动态 (DLL) 版本的运行时库链接的.exe文件一起使用时,用静态(非DLL)版本的运行时库链接的动态链接库可能导致问题
xychzh 2011-05-29
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 loaden 的回复:]
"多线程调试(/MDd)"
不能发布的,试下都用:/MD

另外,如果MD没问题,而MT出问题,则一般是因为在一个模块中删除另一个模块申请的内存。
这里使用智能指针可能是一个比较好的解决办法。
[/Quote]

我这个CHelper其实就是智能指针(我方便提问,所以改名为CHeler),删除的地方一共就一个函数,整个程序只执行了一次delete,重复删除的情况是不存在的,您不用多想了。
另外:
我换成MD也没问题,就MTd有问题.
还有,我在MTd的情况下,把调用CHelper的分配int数据的函数放到dll里执行,也不会崩溃.
ljt3969636 2011-05-29
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 loaden 的回复:]

"多线程调试(/MDd)"
不能发布的,试下都用:/MD

另外,如果MD没问题,而MT出问题,则一般是因为在一个模块中删除另一个模块申请的内存。
这里使用智能指针可能是一个比较好的解决办法。
[/Quote]

我的建议也是你先改成MD让模块用一个堆管理器,看下是不是跨模块管理内存的问题,先排除一下
老邓 2011-05-29
  • 打赏
  • 举报
回复
"多线程调试(/MDd)"
不能发布的,试下都用:/MD

另外,如果MD没问题,而MT出问题,则一般是因为在一个模块中删除另一个模块申请的内存。
这里使用智能指针可能是一个比较好的解决办法。
加载更多回复(9)

64,646

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

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