【问题标题】:What is the "correct" way to reconcile malloc and new in a mixed C/C++ program?在混合 C/C++ 程序中协调 malloc 和 new 的“正确”方法是什么?
【发布时间】:2012-12-02 21:57:56
【问题描述】:

我有一个混合的 C/C++ 程序。它包含一个针对 C 的 flex/bison 解析器,而其余部分是 C++。

作为 C,生成的解析器和扫描器使用 mallocreallocfree 管理它们的内存。它们足以暴露钩子,允许我提交我自己的这些函数的实现。如您所料,(C++)程序的其余部分“希望”使用newdelete 等。

做一些研究似乎表明相关标准并不能保证这种混合应该有效。特别是 C“堆”不一定是 C++“空闲区域”。看来这两个方案可以互相践踏。

除此之外,有一天(很快)这个程序可能会想要集成一个自定义的堆实现,例如 C 和 C++ 使用的 tcmalloc

什么是“正确”的做法?

考虑到集成 tcmalloc(它解释了如何与 C 程序链接)的愿望,我很想在 C++ 内存管理中找到一些跨类型、跨线程、跨一切的重载/挂钩/任何东西。有了它,我可以将所有 C++ 分配/释放调用指向它们的 C 等价物(这反过来又落在 tcmalloc 上。)

这样的泛银河全局 C++ 钩子存在吗?可能它已经在做我想做的事了,类似于ios_base::sync_with_stdio 默认情况下秘密结合 iostream 和 stdio 的方式?

我对讨论 stdio 与 iostreams、切换解析器生成器或使用 C++ flex/bison 骨架(它们引入了独立的问题)不感兴趣。

编辑:请包含支持您的答案的 C++ 标准部分的名称。

【问题讨论】:

    标签: c++ c memory-management malloc new-operator


    【解决方案1】:

    标准确实保证混合两种分配变体会起作用。它不允许允许在来自new 的内存上调用free,因为它们可能为这两种类型使用完全不同的竞技场。

    如果你记得为给定的内存块调用正确的释放函数,你会没事的。如果您遵守规则,他们不会互相践踏,如果您不遵守规则,那么从技术上讲,您正在践踏对方,而不是他们: -)


    C++11 标准的控制部分是20.6.13 C library,它声明,意译:

    • 提供函数callocmallocfreerealloc,基于C标准。
    • 函数不使用::operator new()::operator delete()
    • 这允许传统 C 的东西使用不同的内存领域,而不是正常的 C++ 内存分配。

    鉴于您最终提出的建议,第二个要点很有趣,使用 tcmalloc 替换 C 传统函数并让 C++ 也使用它。

    标准中有一个脚注解释了为什么他们不使用 let malloc() call ::operator new():

    目的是通过调用 std::malloc() 或 std::calloc() 来实现 operator new()。换句话说,他们想要避免循环依赖。

    然而,虽然它允许 operator new() 调用malloc(),但我不确定标准是否真的需要它。因此,为了安全起见,您可能希望将 tcmalloc 注入 C C++ 区域。

    您已经表明您已经知道如何为 C 执行此操作。对于 C++,只需在代码中提供整套全局 operator new()/delete() 函数即可完成,适当编写为在幕后调用 tcmalloc . 3.7.4 Dynamic storage duration 中的 C++ 标准状态:

    该库为全局分配和释放函数提供默认定义。一些全局分配和释放函数是可替换的。

    C++ 程序最多应提供一个可替换分配或解除分配函数的定义。任何此类函数定义都会替换 库中提供的默认版本。

    以下分配和释放函数在程序的每个翻译单元的全局范围内隐式声明:

    • void* operator new(std::size_t);
    • void* operator new[](std::size_t);
    • void operator delete(void*);
    • void operator delete[](void*);

    【讨论】:

    • 这是真的吗?我看到很多混合的报告(和噪音)在谷歌上搜索。 (作为记录,我了解释放 malloc 和删除新闻,并且我相信生成的解析器会自行清理。)
    • 是的,这是真的。我的 Android 上没有标准,但如果您愿意,我会在开始工作后找到相关部分。
    • 很公平,谢谢。您对问题的后半部分有任何见解吗?如果我 LD_PRELOAD malloc/free 到新的实现,这些也会影响 C++ 分配吗?即使标准是沉默的,务实地了解实际编译器的行为也会非常有用。
    • @phs - operator newoperator delete 的标准版本使用 mallocfree 来获取它们的内存。他们不需要为每个分配单独调用,但是用自定义版本替换 mallocfree 将使 C++ 代码使用被替换的版本。好吧,除非你深陷 DLL 地狱。
    • 我知道你回答这个问题已经有一段时间了,但是如果你有时间,你能试着找到规范的相关部分吗?我很想看看!
    【解决方案2】:

    好的。挖出该标准的旧工作草案(2/28/2011 rev 3242。)相关部分似乎是3.7.4 Dynamic storage duration18.6.1 Storage allocation and deallocation

    简而言之,我想要的泛银河钩子似乎是全局 new 和 delete 运算符本身。如果一个人尊重某些语义(在3.7.4.13.7.4.2 中:基本上根据需要委托给new_handler)一个人被允许替换

    void* operator new(std::size_t);
    void* operator new[](std::size_t);
    void operator delete(void*);
    void operator delete[](void*);
    

    阻止整个 C++ 程序的默认内存管理。我仍然找不到证明@paxdiablo 正确的部分,但我愿意暂时使用它。

    【讨论】:

      【解决方案3】:

      您不必担心 Flex 或 Bison 生成的代码分配的任何内存;他们自己处理。其余的,只需使用newdelete。 (而且 Flex 和 Bison 能够生成某种 C++。总的来说,我发现通过一个简单的脚本运行输出,使用 sed 足以使它们的输出真正与 C++ 兼容。)

      【讨论】:

        猜你喜欢
        • 2019-07-31
        • 1970-01-01
        • 2021-11-24
        • 1970-01-01
        • 2011-01-15
        • 1970-01-01
        • 2017-05-08
        • 2015-11-01
        • 2020-05-24
        相关资源
        最近更新 更多