LocalAlloc 与 GlobalAlloc 与 malloc 与 new

Posted

技术标签:

【中文标题】LocalAlloc 与 GlobalAlloc 与 malloc 与 new【英文标题】:LocalAlloc Vs GlobalAlloc Vs malloc Vs new 【发布时间】:2016-03-23 11:07:46 【问题描述】:

我已经在各种链接上搜索过这个,但仍然存在疑问。

我不明白LocalAlloc vs GlobalAlloc vs malloc vs new 内存分配的区别。

我浏览过MSDN的这个链接:

Comparing Memory Allocation Methods

请解释以下语句:

ma​​lloc 函数的缺点是依赖于运行时。 new 运算符的缺点是依赖于编译器和语言

【问题讨论】:

LocalAllocLocalFree 在您需要在一个模块(DLL 或 EXE)中分配内存并在单独的模块中释放它时很有用。除非您使用相同的 MSVCRT DLL 集链接两个模块,否则调用 freedelete 可能会崩溃,因为内存是由不同的运行时实例分配的。如果 API 提供者不想直接公开解除分配函数,那么拥有 LocalFree 往往是一个简单的故事。 docs.microsoft.com/en-us/windows/win32/memory/… 【参考方案1】:

摘自Raymond Chen's OldNewThing

在 16 位 Windows 时代,差异是显着的。

在 16 位 Windows 中,内存是通过调用的值访问的 “选择器”,每个都可以寻址高达 64K。有一个 默认选择器称为“数据选择器”;所谓的操作 “近指针”是相对于数据选择器执行的。为了 例如,如果您有一个近端指针 p,其值为 0x1234 并且您的 数据选择器是 0x012F,那么当你写 *p 时,你正在访问 内存在 012F:1234。 (当你声明一个指针时,它就在附近 默认。如果你想要一个远指针,你必须明确地说 FAR。)

重要提示:近端指针总是相对于选择器,通常 数据选择器。

GlobalAlloc 函数分配了一个选择器,可用于 访问您请求的内存量。你可以访问内存 在那个带有“远指针”的选择器中。 “远指针”是一个选择器 结合近指针。 (请记住,近指针是 相对于选择器;当您将近指针与 合适的选择器,你会得到一个远指针。)

程序和 DLL 的每个实例都有自己的数据选择器,已知 作为 INSTANCE。因此,如果你有一个近端指针 p 并访问 它通过 *p 来自程序可执行文件,它访问的内存相对于 程序实例的 HINSTANCE。如果你从 DLL 访问它,你 获得了相对于你的 DLL 的 HINSTANCE 的内存。

因此,在 16 位 Windows 中,LocalAlloc 和 GlobalAlloc 功能完全不同! LocalAlloc 返回了一个近 指针,而 GlobalAlloc 返回一个选择器。

您打算在模块之间传递的指针必须位于 “远指针”的形式,因为每个模块都有不同的默认值 选择器。如果您想将内存所有权转让给另一个 模块,你必须使用 GlobalAlloc 因为它允许接收者 调用 GlobalFree 来释放它。

即使在 Win32 中,也必须小心不要混淆本地堆 从全局堆。从一个分配的内存不能在 其他。所有关于近和远指针的怪异消失了 过渡到 Win32。但是局部堆函数和全局 然而,堆函数是两个不同的堆接口。

另外,您指定的link 明确表示,

从 32 位 Windows 开始,GlobalAlloc 和 LocalAlloc 是 实现为使用句柄调用 HeapAlloc 的包装函数 进程的默认堆,可以指示 HeapAlloc 提升 如果无法分配内存,则异常,无法分配功能 可与 LocalAlloc 一起使用。

对于您对 ma​​lloc 与 new 的混淆,Billy ONeal 的回答非常清楚地总结了这一点。

对于malloc and HeapAlloc的区别, David Heffernan 和 Luis Miguel Huapaya 的回答结合起来给出了完美的解决方案:

malloc 是可移植的,是标准的一部分。 malloc(和其他 C 运行时堆函数)依赖于模块,这意味着如果您在一个模块(即 DLL)的代码中调用 malloc,那么您应该在同一模块的代码中调用 free,或者您可能会遭受一些非常严重的堆损坏。 HeapAlloc 不可移植,它是一个 Windows API 函数。使用HeapAllocGetProcessHeap 而不是malloc,包括重载newdelete 运算符来利用这些,允许您在模块之间传递动态分配的对象,而不必担心内存损坏时内存损坏一旦指向内存块的指针被传递到外部模块,在一个模块的代码中分配并在另一个模块的代码中释放。

【讨论】:

我认为这是一篇好文章(毕竟,我在您发布此答案之前大约 7 分钟链接到它)但我认为它没有回答用户的问题,这是专门谈论语言“依赖于运行时”和“依赖于编译器和语言”。 当然,你的回答概括了一切。我刚刚从该博客中摘录了准确、相关和简短的摘录。 不,我不认为博客回答了用户的问题。该博客是关于 Global/LocalAlloc 的。用户的问题是关于 malloc 和 new 的问题,文章什么也没说。 谢谢,您的解释简单明了。很容易抓住。熟悉远近指针的人甚至不需要阅读雷蒙德的博客。尊重。 @User1234:: 我也是 :)【参考方案2】:

GlobalAlloc and LocalAlloc are old functions from the 16 bit era. 不同之处在于您有时必须能够分配仅在您的段中使用的内存(使用近指针),有时需要分配内存以与系统上的其他进程和段共享。今天,这些家伙以某种形式转发到 HeapXxx 函数,例如HeapAlloc。如果您正在编写新代码并且需要避免与 C 运行时链接,则应改用 HeapXxx 函数。当然,如果你调用其中任何一个,你的程序将只能在 Windows 上编译和运行。

malloc 是“依赖于运行时”的,因为使用它需要链接到 C 运行时 (CRT)。 CRT 是包含所有其他标准 C 库函数的库,例如 printfqsort。你可以编写一个普通的 Win32 API 程序而不用链接(但老实说,我不明白你为什么要在真正的软件中这样做)。

new 依赖于编译器和语言,因为它们需要可以编译 C++ 的编译器。 (而且通常new 是根据malloc 实现的,因此可能还需要使用CRT)

【讨论】:

谢谢比利·奥尼尔 :)。 new 可以抛出异常。使用 Visual Studio(以及其他一些编译器)进行编译时,异常处理需要 CRT。因此,即使 new 没有按照 malloc 实现,您仍然会依赖 CRT。 @IInspectable 不一定;分配失败时,用户可以覆盖 terminate() 的 new。 我拿了你的 "通常new 是按照malloc" 来实现的,意思是你在谈论 C++ 标准库的实现,而不是用户-提供过载。 @IInspectable 确定;但是如果你调用内置的,你已经依赖于 CRT,所以异常并不是什么大问题:)

以上是关于LocalAlloc 与 GlobalAlloc 与 malloc 与 new的主要内容,如果未能解决你的问题,请参考以下文章

new,malloc,GlobalAlloc具体解释

关于Clipboard和GlobalAlloc函数的关系

C++内存分配秘籍—new,malloc,GlobalAlloc详解

demo

demo

demo