【问题标题】:Does C++ runtime always require malloc()?C++ 运行时是否总是需要 malloc()?
【发布时间】:2015-05-16 04:25:16
【问题描述】:

我有一个运行裸机的 C++ 应用程序,我想让它尽可能小。

我没有在任何地方使用动态内存分配。我没有使用 STL 函数。我还用空函数覆盖了所有种类的“删除”和“新建”。尽管如此,当我查看已排序的符号列表时,我发现 malloc() 仍然是我编译的二进制文件中最大的项目之一。如果我能摆脱它,我可以将我的二进制文件缩小大约 25%。

C++ 运行时通常需要 malloc() 来进行幕后类型的工作吗?

(如果重要的话,我正在使用 Xilinx 的 gcc fork for Microblaze 架构)

【问题讨论】:

  • 可以通过提供 malloc() 和 free() 的虚拟空定义来减少 C++ 开销
  • 这是一个很好的观点。不过,这可能会破坏运行时本身吗?
  • 如果不使用堆应该没问题。查看这篇文章embedded.com/design/mcus-processors-and-socs/4007134/…
  • 可能:你考虑过动态链接吗?
  • 除非您使用诸如 xilfs 或 lwip 之类的高级库(即使在那里,我不确定他们是否使用 malloc),否则这不是必需的。您始终可以 objdump 并验证 malloc 的使用位置,对于裸机应用程序来说应该不会太长。

标签: c++ malloc xilinx standard-library bare-metal


【解决方案1】:

程序对malloc() 的依赖可以在C 和C++ 中出现,即使程序不直接使用它们。这是编译器和标准库的实现质量问题,而不是标准的要求。

这实际上取决于编译器启动代码(设置事物以便可以调用 main() 的代码)的工作方式以及标准库代码的实现方式。

例如,在 C 和 C++ 中,启动代码(在托管环境中)需要收集有关命令行参数的信息(可能复制到某些分配的缓冲区),连接到标准文件/流(如 std::cout 和 @987654324 @ 在 C++ 中,在 C 中是 `stdout 和 stdin)。任何这些事情都可能涉及动态内存分配(例如,与标准流相关的缓冲区)或执行程序实际不需要的代码。

【讨论】:

    【解决方案2】:

    C++ 有两种实现方式,托管的和独立的。托管实现确实假设存在malloc,并且经常将其用于内部目的。独立实现假设只存在 new 函数,因为它支持 C++ 关键字 new,但很容易确保不会调用此函数。

    两者之间的区别在于,在独立实现中,您可以控制程序启动,并且所需的头文件和库集是有限的。通过设置入口点来控制程序的启动。

    g++ -ffreestanding -e _entry program.cpp
    

    program.cpp 可能是:

    extern "C" int entry()
    {
        return 0;
    }
    

    extern "C" 是防止 C++ 名称修改所必需的,这可能会导致在链接过程中难以弄清楚 entry 的名称是什么。那么,不要使用newstd::string、流式I/O、STL等,避免使用at_exit

    这应该使您能够控制启动和清理代码,并限制编译器可以隐式依赖的标准库中可用的内容。但是请注意,这可能是一个具有挑战性的环境。您不仅会阻止初始化堆、I/O 流等,还会阻止设置异常、RTTI 和调用静态存储对象构造函数等。您将不得不编写代码或使用库来手动选择您可能想要使用的 C++ 的几个功能。如果你走这条路,你可能想仔细阅读这个 wiki http://wiki.osdev.org/C%2B%2B。您可能希望至少调用全局构造函数,这很容易做到。

    说明

    标准

    C++ 具有“独立实现”的概念,其中可用的标头更少。

    3.6.1 主函数[basic.start.main]

    1 [snip] 独立环境中的程序是否需要定义 main 函数由实现定义。

    17.6.1.3 独立实施 [合规]

    1 定义了两种实现:托管和独立 (1.4)。对于托管实现,这 国际标准描述了一组可用的标头。

    2 独立实现具有一组实现定义的标头。该集合应至少包括 表 16 中显示的标题。

    3 提供的标头 版本应至少声明函数 abort、atexit、at_quick_exit、exit 和 quick_exit (18.5)。 [剪辑]

    表16列出ciso646cstddefcfloatlimitsclimitscstdintcstdlibnewtypeinfo、@98765434@2@、@98765434@2 cstdaligncstdargcstdbooltype_traitsatomic

    以上大多数标头都包含类型、常量和模板的简单定义。唯一可能看起来有问题的是typeinfoexceptioncstdlibnew。前两个分别支持 RTTI 和异常,您可以确保通过附加编译器标志禁用它们。 cstdlibnew 您可以通过不调用 exit/at_exit 并且不使用 new 表达式来简单地忽略。避免at_exit 的原因是它可能在内部调用new。独立片段中的任何其他内容都不应调用 cstdlibnew 中的任何内容。

    选项

    上面最重要的选项是-e _entry,它可以让您控制程序启动时运行的内容。

    -ffreestanding 告诉编译器和标准库(更确切地说,它的独立片段)不要假设整个标准库都存在(即使它仍然存在)。它可以防止生成令人惊讶的代码。请注意,此选项实际上并不限制 可以使用哪些标头。例如,您仍然可以使用iostream,但如果您还更改了入口点,这可能不是一个好主意。它的作用是防止支持 freestanding 标头的代码调用 freestanding 标头之外的任何内容,并防止编译器隐式生成任何此类调用。

    【讨论】:

      【解决方案3】:

      如果您使用任何STL,例如std::libstd::map。甚至std::cout,幕后都会有动态内存分配。

      【讨论】:

        【解决方案4】:

        它总是需要malloc。因为必须加载二进制文件以及共享库。

        【讨论】:

        • 那么,在二进制文件中有一个malloc的副本,以便在加载二进制文件的过程中使用它?
        • 没有。在调用 main 之前,也必须先加载二进制文件。一些结构/映射必须填充有关加载的共享对象和可执行文件的信息,甚至在调用 main 之前。他们将在内部调用 malloc/calloc 来创建这些结构/映射。但我并不是说 malloc 需要静态链接到二进制文件。但是没有malloc,程序永远无法运行。所以,C++ 运行时总是需要 malloc/calloc
        猜你喜欢
        • 2013-03-08
        • 1970-01-01
        • 2018-02-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-11
        相关资源
        最近更新 更多