【问题标题】:C++ on Small-Footprint Microcontrollers小尺寸微控制器上的 C++
【发布时间】:2011-08-08 07:56:52
【问题描述】:

在我看来,人们一直在回避,或者更确切地说,强烈反对在微控制器上使用 C++,但我终其一生都无法弄清楚原因。如果您远离大型 C++ 库(例如 STL)并且不尝试使用 RTTI 或异常处理等复杂功能,那么 C 与 C++ 之间真的有任何明显的区别吗?虚拟继承对复杂性或占用空间有巨大影响吗?我认为这会是一些额外的内存,但大部分复杂性将由编译器处理,但话说回来,我对那个黑魔法知之甚少。我只是不明白为什么人们非常坚持使用 C,除了可能没有 C++ 编译器的少数架构(如果有的话)。即使您不能使用 cin 或 cout,模块化和模板的好处似乎也是显而易见的。

我问是因为我正在为一些我想从事的业余项目做一些研究。理想情况下,我想严格使用 C++,以便能够很好地模块化事物,而不是 C 的“SomeClass_SomeMethod(struct object* this ...)”方法来实现“面向对象”。 (对于这些项目,我更喜欢 object Pascal,但是对那种语言的支持并不完全是一流的......)我宁愿避免转向功能更强大的微处理器,因为 A. 对于我正在做的项目,我不需要大量资源。我不打算编写 60 个状态卡尔曼滤波器或编码 1080p 视频 B。(真正的踢球者)我想使用 DIP 和 QFP 封装中可用的处理器。我希望无需在我的烤箱中焊接或烘烤任何东西即可制作原型。

有什么想法吗?

【问题讨论】:

    标签: c++ oop embedded


    【解决方案1】:

    对于“占用空间小”,我担心的是代码膨胀。如果您的代码需要驻留在一小块硬件中,可以使用模板类的实例

     std::vector<int>
    

    有自己的一套指令,独立于

     std::vector<double>
    

    因此,每次您创建一种新的向量时,编译器都会有效地复制粘贴代码以创建新类,并使用自己的一组函数复制每条指令。如果您对存储指令的内存量有限制,这可能会很快成为问题。对于非嵌入式系统上的人来说,它会成为问题。

    不过,就运行时性能而言,我认为您没有太多to worry about。在某些情况下,例如排序,C++ outperforms C

    【讨论】:

    • 当然,现代 C++ 链接器在二进制级别检查重复函数,而 STL 实现者知道如何使用它。结果,std::vector&lt;int&gt;::clear() 可能与std::vector&lt;double&gt;::clear() 折叠,因为两者都释放所有内存,不调用析构函数。
    • @MSalters,是的,但你不能消除所有重复,而且其中一些定制微控制器必须使用不同质量水平的商业编译器。
    • 您也无法消除手卷集合中的所有重复项。他们同样受到糟糕的编译器的影响。
    • @MSalters,那么向量可能是一个坏例子。更像 boost::assign::list_of 的东西,你可以在不重复代码的情况下获得相同的功能
    【解决方案2】:

    当然变化很大。

    我不会在“小型”MCU 上使用虚拟继承。我什至根本不会使用堆。

    C++ 在该领域最具吸引力的特性是命名空间(用于在联网 MCU 的程序之间共享软件组件)、模板(例如,通过 I/O 端口参数化协议)和通用语义改进,如 static_cast并且不那么粗略的积分提升。

    但是,至少在我短暂涉足专业嵌入式领域时,根本不存在合适的 C++ 编译器,而且可用的蹩脚编译器每年要花费数千美元。

    GCC 是可用于嵌入式平台的功能最强大、应用最广泛的 C++ 编译器。但是,它的平台支持很不平衡。如果您拥有无限的资源,EDG 会宣传他们将为“您的”嵌入式平台带来优于 Comeau 的支持。

    【讨论】:

    • TI 免费为 MSP430 和 28xx DSP 提供 C++。
    【解决方案3】:

    C++ 委员会就此主题写了一封(免费)technical report

    【讨论】:

      【解决方案4】:

      C++ 的人经常问“你为什么使用 C 而不是 C++”。我想知道为什么我应该使用 C++ 而不是 C。

      首先,我们必须意识到这两种语言都是古老的,并且都有可怕的语法。争论似乎经常集中在“你应该使用 C++,因为 C++ 是现代的而 C 是旧的”。实际上,争论是关于一个人最喜欢的恐龙肉味道。人们没有要求一种适合嵌入式的现代语言,而是宣扬 C++,它只是一种具有 C 兼容性的奇怪的临时混合语言,以等待发明更好的语言。

      其次,有一种说法是 C++ 是面向对象的,而 C 不是。流行语面向对象归结为三件事:

      • 1) 具有自主对象的模块化设计,不会与其他任何东西紧密耦合。这是任何程序的一个非常重要的属性。
      • 2) 数据的私有封装和缩小的数据范围。这是任何程序的一个相当重要的属性。
      • 3) 类的多态性:继承其他类并在继承时表现不同的类。多态性非常有用,但在小型嵌入式系统中则不然。

      1) 在 C 和 C++ 中都可以完全实现,这是程序设计而不是语言语法的问题。这是迄今为止最重要的 OO 属性!不幸的是,任何语言标准都没有告诉您如何设计程序。 C++ 中没有任何东西会自动带来更好的模块化设计,这一切都掌握在程序员手中。

      2) 可以在 C 和 C++ 中实现。两种语言都缩小了数据范围。在 C 中,私有封装是通过在文件范围变量上使用 static 关键字的有点可怕的语法来完成的。在 C++ 中,使用 private/protected 可以更优雅地完成。

      3) 可以在 C 和 C++ 中实现。这两种语言都有 horrible 语法。在 C 中,你会摆弄结构和函数接口来实现它。在 C++ 中,您可以通过继承和使函数“虚拟”以不那么可怕的方式做到这一点。然而,C++ 语法和所需的实现仍然是一团糟,尽管比 C 方式好一点。

      这两种语言都不会以漂亮、优雅的方式为您提供与 OO 相关的内容。 C++ 从不那么讨厌的语法中获得了什么,当您开始涉足未定义/未指定/实现定义的行为时,它就会失去。

      整个 OO 论点似乎没什么大不了的,C++ 在 OO 方面并不是一个巨大的改进。那么我在嵌入式系统中还需要 C++ 中的哪些内容呢?有一件事很突出:标准化的内联汇编语法。对于嵌入式系统,这可能是 C++ 相对于 C 的最大优势。

      除此之外,异常、STL、模板、RTTI、overator 重载、函数重载等,或多或少都是没有用的功能。

      最后,现实给你一记耳光:很少有嵌入式编译器能够按照标准完全实现 C++。

      【讨论】:

      • 年龄与它无关,两者都是系统级语言,这是大多数嵌入式开发所需要的;当然是硬件级别的访问。它们当然不是唯一的此类语言,它们也不是最古老的此类语言。
      • 此外,当您只有一块裸芯片时,对于大多数架构而言,唯一的语言选择通常是本机汇编程序、C 或 C++。在裸机系统上对其他语言的支持通常是不可用的,而 C 至少无处不在,而 C++ 也同样如此。因此,这些通常是引导语言的唯一选择。例如,C# 需要 .NET Micro 或 WinCE 才能在嵌入式系统上运行,而 Java 需要 JVM。
      • 年龄与它无关? C是40岁,C++是30岁。它们都是可怕的语言。您还在使用 70 年代初的 CPU 吗?为什么不再开发编程语言?我们应该需要适合嵌入式的新的现代语言。 Java 在这方面是一场惨败,而 C# 甚至没有成为通用语言的野心。尽管如此,这些语言在语法方面还是比 C/C++ 好得多。它们不能用于实时嵌入式系统,只是因为它们需要运行时平台。
      • 我不同意关于多态性和小型嵌入式设备的评论:尤其是在嵌入式应用程序中,尽可能将应用程序代码与硬件需求分开是有利的。您可以拥有一个相当小的微控制器,它能够执行复杂的任务,保证目标硬件的模拟和单元测试。在 C++ 中,这种抽象是通过一个接口的多个实现来实现的;在 C 中,这通常是通过“文件级多态性”和不明确的指针传递来实现的。考虑到这些选项,C++ 应该是 ASM 或 C 的明显赢家。
      • 您喜欢哪种语言?对内联汇编器的支持似乎排除了与 C 系列有很大语义差异的语言。你的批评主要是关于语法的,但我们有能力处理丑陋的语法。考虑英语! (要进行良好的心理锻炼,请采用您拥有的任何标准并评估 FORTH。)
      【解决方案5】:

      在 C++ 中,您基本上只需为超出 C 可编译代码所使用的内容付费,并且一些额外内容是免费的。

      一些针对小型目标的 C++ 编译器的最大问题是可用 C++ 实现的完整性或 C++ 编译器的可用性。

      EETimes/Embedded.com 多年来发表了许多关于该主题的文章:

      这些文章中的大多数观点是,您不必在嵌入式系统中使用所有的 C++,它们会根据内存、速度和各种特性的确定性来衡量或解释成本.您使用哪些部分取决于您的应用程序的性质(例如,它是否具有实时约束)以及目标平台的可用资源和性能。

      【讨论】:

      • +1 供 Dan Saks 参考。我必须参加他的 SDWest'09 讲座“C++ on Bare Metal”。关于在嵌入式项目中使用 STL 和模板的重要信息。
      • Saks 对 C++ 来说是一个绝望的传教士,但希望他的文章非常有偏见。
      • 无论应用程序如何,都倾向于在 C 之前使用 C++。
      • @Lundin:读者有责任提出意见,但没有适用于 C 的应用程序 C++ 也不可用,而且 C++ 是一个更大的工具包,所以有什么不喜欢的?当然,更多的工具会提供更多的机会来不恰当地使用它们,并切断你的腿,但这也许就是这个问题(和 Dan 的文章)的意义所在。
      • 感谢 EETimes 断开所有链接 - 已修复(暂时)。如果再次损坏,只需搜索标题/作者。
      【解决方案6】:

      C 与 C++ 之间真的有任何明显的区别吗?

      根据我的经验,RAM 的使用存在很大差异。

      例如:我目前正在为具有 512KB FALSH 和 64KB RAM 的 ARM uC 开发 C++。 RAM 使用量应该小于 30kB,但是是两倍,因为每个 const 最终都在 RAM 中。 这是因为几乎不可能(至少对于 GCC 和 ARM 目标)说服编译器将 const 类成员保留在 FLASH 中。实现这一点的唯一方法是使用无构造函数的类,将所有 const 成员声明为 public 并使用聚合初始化列表。

      更糟糕的是,C++ 不允许像在普通 C 中那样在初始化列表中命名成员:

      结构圣{int b;诠释一个[3]; }; 静态 const struct St st_array[2] = { [1] = { .a = {1,2,3,}, .b = 10, }, // 故意乱序 [0] = { .b = 8, .a = { 4,5,6,}, }, // 说明名称的值。 };

      所有 C 编译器都会将这些常量放入“常量数据”段(在 FLASH 中)。

      在 C++ 中你必须这样做:

      class ClassA // 不能有构造函数或析构函数 { public: // const 数据不能是私有的,用于聚合初始化 常量 int b; 常量 int a[3]; 私人的: int priv_fn(int i); 民众: int pub_fn(); }; 静态 ClassA classA_array[2] = { { 3, { 8, 9,10,}, }, // 聚合初始化 { 4, { 9,10,11,}, }, // 最好正确排序!!! };

      根据您的编译器,即使这样也不能保证常量保留在 FLASH 中。

      是的,我知道,在 C++0x 中,您可以将初始化列表与构造函数一起使用,这就是我正在做的事情,但是当您有一个在运行时调用的构造函数时,所有初始化都会变成动态的。

      technical report(感谢 MSalters)证实了这一点:

      7.1.2 构造函数和 ROMable 对象 一般来说,具有构造函数的类的 const 对象必须动态初始化。 但是,在某些情况下,如果静态分析...

      最重要的是,这种静态分析不是由我可用的任何编译器完成的,如果我必须将自己限制为具有公共常量且没有初始化程序命名的无构造函数类,那么我不妨用普通的(和对象面向) C.

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-10-05
        • 2011-04-18
        • 2017-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多