RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 636454
Accepted
Harry
Harry
Asked:2020-03-07 17:09:16 +0000 UTC2020-03-07 17:09:16 +0000 UTC 2020-03-07 17:09:16 +0000 UTC

内存进出DLL

  • 772

关于这个问题...

我感兴趣的是如何真正使用 DLL 中的内存,尤其是释放 DLL 后动态内存中剩余的内容。

创建了一个简单的 DLL,它具有make_array在动态内存中创建数组的功能。VC++,静态链接。

然后我这样嘲笑:

dll = LoadLibrary("Dll.dll");
memfunc m = (memfunc)GetProcAddress(dll,"make_array");
int * a = m(5);
for(int i = 0; i < 5; ++i) cout << a[i] << " "; cout << endl;
FreeLibrary(dll);

for(int i = 0; i < 5; ++i) cout << (a[i] *= a[i]) << " "; cout << endl;

dll = LoadLibrary("Dll.dll");
m = (memfunc)GetProcAddress(dll,"make_array");

for(int i = 0; i < 5; ++i) cout << (a[i] *= a[i]) << " "; cout << endl;

delete[] a;
a = m(6);
for(int i = 0; i < 5; ++i) cout << a[i] << " "; cout << endl;

好吧,即 我检查了是否可以删除程序中的内存,以及卸载 DLL 后内存是否仍然可用。事实证明,如果它们是由一个编译器创建的,那么一切都很好,内存管理器是否足够聪明?

我对 Open Watcom 也做了同样的事情——结果是一样的。

但是如果 DLL 来自 OW,而程序来自 VC++,那么除了delete[]. 当然,正如人们所期望的那样——因为不同的内存管理器。

不,我知道内存分配在哪里 - 在那里释放它:)

但是有两个问题我无法回答。

如果内存是由DLL中的内存管理器分配的,那么通过什么机制来避免两个管理器争吵呢?DLL 是否为调度程序分配了自己的内存区域?但是据我了解,在卸载 DLL 时,该区域仍未发布?

如何实际使用智能指针之类的东西?就像,在 DLL 调用make_shared中,其结果随后在程序中使用 - 如何保证 DLL 中的最后删除?出现的一切都太做作了。是否可以从 DLL 中调用删除器?

PS好吧,我没有认真地使用DLL,我没有深入挖掘......

c++
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. Best Answer
    KoVadim
    2020-03-07T17:29:35Z2020-03-07T17:29:35Z

    您需要了解的第一件事是运行时。就工作室而言,有MD和MT。在第一种情况下,使用公共运行时。如果你在一个 dll 中创建一个类并在另一个中删除它,一切都会按预期工作。如果每个人都有自己的运行时,那就更难了。如果你在一个地方创建它并在另一个地方删除它,它可能会工作(如果内存管理器足够聪明),它可能会工作,但稍后会崩溃(如果 dll 和程序使用相同的运行时),或者它可能会立即崩溃(如果运行时不同)。

    应用程序如何处理内存?通常,内存管理器(它是运行时的一部分)从操作系统请求一大块内存(4Mb 甚至 64Mb),并且在其中已经“切割”成使用 new / malloc 创建的小对象。卸载应用程序时,此内存将返回给操作系统。

    但是据我了解,在卸载 DLL 时,该区域仍未发布?

    如果运行时是动态的,那么内存管理器是共享的。而这个内存是由应用程序自己管理的。

    如果使用静态运行时,那么一切都取决于程序员和运行时。dll 知道它正在被卸载并且“应该”释放内存。

    您实际上是如何使用智能指针之类的东西的?

    智能指针在这种情况下是个好帮手。您只需要确保他们的“自定义删除器”拼写正确(我什至不知道它在俄语中的用法 - 自定义删除器?或自定义删除功能?)。在这种情况下,将在正确的内存管理器中调用正确的函数(无论如何,程序员都有机会破坏一切)。

    但是,如果 DLL 来自 OW,而程序来自 VC++,那么除了 delete[] 之外,一切正常。当然,正如人们所期望的那样——因为不同的内存管理器。

    这是一个棘手的情况。调用时delete[],内存管理器需要知道要删除多少内存。这个数字需要存储在某个地方。而且我确信 studio 和 gcc 使用不同的、不兼容的模型。因此,如果这样的指针在具有不同管理器的 dll 之间传递,绝对可能发生任何事情。

    • 8
  2. VladD
    2020-03-07T17:46:28Z2020-03-07T17:46:28Z

    看。

    问题是 C++ 没有二进制标准。这意味着同一个类,当由不同的编译器编译时(是的,即使由具有不同标志的相同编译器编译时),可以有不同的二进制布局。虚方法表可能有不同的表示,方法名称的不同修饰,或者仅仅是不同的物理大小。因此,在不同编译器或同一编译器的不同版本编译的组件之间传递这种比简单结构更复杂的东西,自然会出现问题并践踏别人的记忆。在困难的情况下,指定#pragma pack.

    这解释了为什么从 DLL 导出的头文件通常包含extern "C".

    如果保证DLL被相同版本的编译器用相同的开关编译,那么就可以传递对象。但这会立即引入版本控制问题:更新组件会返回问题。


    这就解释了为什么如果不是项目中的所有 DLL 都是用相同版本的编译器编译的,就不可能通过智能指针传递对象。(还有为什么系统 DLL 不使用智能指针、异常和其他 C++ 功能。)


    接下来,关于内存所有权的问题。这部分是 Microsoft 特有的。DLL 编译具有静态和动态链接运行时库的模式。

    在静态运行时链接的情况下,您有自己的实现new,并且delete在每个 DLL 中,它们彼此之间一无所知,并且具有非重叠堆。这意味着您必须确保该对象由分配它的同一运行时释放。

    在运行时动态链接的情况下,每个应用程序都有一个运行时 - 但前提是您的应用程序是使用相同版本的工作室编译的!在这种情况下,您可以在一个 DLL 中分配一个对象,然后在另一个 DLL 中释放。在最坏的情况下,当您拥有由不同工作室版本编译的不同 DLL 时,它们引用不同版本的运行时库,在这种情况下,对于编译其 DLL 的工作室的每个版本,您将在运行程序中拥有一个运行时. 后果很明显。

    • 6
  3. Daniel Protopopov
    2020-03-07T17:24:52Z2020-03-07T17:24:52Z

    在我看来,这是很好的覆盖:

    1. 对称所有权的语义。如果 DLL 返回指向动态分配的内存块的指针,则用户必须调用适当的释放器来执行此操作。

    当然,如果它们不匹配,就会出现问题 - 对于这种情况,建议使用用于分配和释放已加载库中已分配内存区域的函数(dll->free() 或 dll->freeArray( ))

    1. 灵活性。如果用户想在其他地方分配内存——不一定在堆中,而是在其他地方。

    也就是说,最终,将指向内存的指针传递给已分配内存的 DLL 函数将比在 DLL 本身中分配内存更好。

    • 3

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5