是否有任何理由使用C而不是C ++进行嵌入式开发?

时间:2009-05-01 18:51:59

标签: c++ c embedded c89

问题

我的硬件C ++和C89上有两个编译器

我正在考虑将C ++与类一起使用但没有多态(以避免使用vtable)。 我想使用C ++的主要原因是:

  • 我更喜欢使用“内联”函数而不是宏定义。
  • 我想使用命名空间,因为我的前缀会使代码混乱。
  • 我认为C ++有点类型更安全,主要是因为模板和详细的转换。
  • 我真的很喜欢重载的函数和构造函数(用于自动转换)。

在开发非常有限的硬件(4kb RAM)时,您是否认为有理由坚持使用C89?

结论

感谢您的回答,他们真的很有帮助!

我认为这个主题通过,我会坚持使用C主要是因为:

  1. 在C中预测实际代码更容易,如果你只有4kb的内存,这一点非常重要。
  2. 我的团队主要由C开发人员组成,因此不会经常使用高级C ++功能。
  3. 我找到了一种在C编译器中内联函数的方法(C89)。
  4. 由于你提供了很多好的答案,很难接受一个答案。 不幸的是我不能创建一个wiki并接受它,所以我会选择一个让我最想的答案。

31 个答案:

答案 0 :(得分:63)

对于非常资源受限的目标,例如4KB的RAM,我会在做出大量努力之前用一些样本测试水域,这些努力无法轻易移植回纯ANSI C实现。

嵌入式C ++工作组确实提出了该语言的标准子集和标准库的标准子集。不幸的是,当C用户期刊去世时,我忘记了这种努力。看起来Wikipedia上有一篇文章,committee仍然存在。

在嵌入式环境中,你必须要小心内存分配。要强制执行该护理,您可能需要将全局operator new()及其朋友定义为无法链接的内容,以便您知道它未被使用。另一方面,放置new很可能是您的朋友,在明智地使用稳定,线程安全和延迟保证的分配方案时。

内联函数不会造成太大问题,除非它们足够大以至于它们本来应该是真正的函数。当然,他们取代的宏也有同样的问题。

模板也可能不会导致问题,除非他们的实例化运行失败。对于您使用的任何模板,请审核生成的代码(链接映射可能有足够的线索),以确保只发生您打算使用的实例。

可能出现的另一个问题是与调试器的兼容性。对于其他可用的硬件调试器来说,对与原始源代码的交互提供非常有限的支持并不罕见。如果你有效地必须在程序集中进行调试,那么C ++的有趣名称错误会给任务增加额外的混乱。

RTTI,动态强制转换,多重继承,重度多态和异常都会带来一些运行时成本。如果使用这些功能,其中一些功能会超过整个程序,其他功能只会增加需要它们的类的权重。了解差异,并在充分了解至少粗略的成本/效益分析的情况下明智地选择高级功能。

在小型嵌入式环境中,您可以直接链接到实时内核,也可以直接在硬件上运行。无论哪种方式,您都需要确保运行时启动代码正确处理C ++特定的启动杂项。这可能就像确保使用正确的链接器选项一样简单,但由于通常可以直接控制源到上电复位入口点,因此您可能需要对其进行审核以确保它能够完成所有操作。例如,在我工作的ColdFire平台上,开发工具附带了CRT0.S模块,该模块具有C ++初始化程序,但注释掉了。如果我直接从盒子中使用它,我会被构造函数从未运行过的全局对象弄糊涂。

此外,在嵌入式环境中,通常需要在硬件设备可以使用之前对其进行初始化,如果没有操作系统且没有引导加载程序,那么您的代码就是这样做的。您需要记住,在调用 main()之前运行全局对象的构造函数,因此您需要修改本地CRT0.S(或其等效项)以完成硬件初始化< em>之前调用全局构造函数本身。显然,main()的顶部太晚了。

答案 1 :(得分:42)

使用C over C ++的两个原因:

  1. 对于很多嵌入式处理器,要么没有C ++编译器,要么需要额外付费。
  2. 我的经验是,有很大比例的嵌入式软件工程师很少或根本没有C ++经验 - 或者因为(1),或者因为它不会被教授电子工程学位 - 所以它会更好坚持他们所知道的。
  3. 此外,原始问题和一些评论提到了4 Kb的 RAM 。对于典型的嵌入式处理器,RAM的数量(大部分)与代码大小无关,因为代码存储起来,并从闪存运行。

    当然,代码存储空间的数量是需要考虑的因素,但随着新的,更宽容的处理器出现在市场上,除了对成本最敏感的项目之外,它不再是以前的问题。

    关于使用C ++子集与嵌入式系统一起使用:现在有一个MISRA C++标准,值得一看。

    编辑:另请参阅this question,这引发了关于嵌入式系统的C与C ++的争论。

答案 2 :(得分:25)

没有。在进行嵌入式开发时,可以避免任何可能导致问题的C ++语言特性(运行时多态性,RTTI等)。有一个嵌入式C ++开发人员社区(我记得在旧的C / C ++用户期刊中使用C ++阅读嵌入式开发人员的专栏),我无法想象如果选择那么糟糕,他们会非常直言不讳。

答案 3 :(得分:20)

Technical Report on C++ Performance是这类事情的一个很好的指南。请注意,它有一个关于嵌入式编程问题的部分!

另外,++提到了嵌入式C ++的答案。标准不是100%的我的口味,但在决定你可能会丢弃C ++的哪些部分时,这是一个很好的参考。

在为小型平台编程时,我们禁用异常和RTTI,避免虚拟继承,并密切关注我们所处的虚拟函数的数量。

你的朋友是链接器地图:经常检查它,你会很快发现代码和静态内存膨胀。

之后,标准的动态内存使用注意事项适用:在您提到的限制环境中,您可能根本不想使用动态分配。有时您可以使用内存池来获取小动态分配,或者“基于帧”的分配,在这种情况下,您可以预先分配块并在以后丢弃整个块。

答案 4 :(得分:16)

我建议使用C ++编译器,但限制使用C ++特定功能。您可以在C ++中编写类似C的程序(在执行C ++时包含C运行时,但在大多数嵌入式应用程序中,您仍然不使用标准库)。

您可以继续使用C ++类等,只需

  • 限制使用虚拟功能(正如您所说)
  • 限制使用模板
  • 对于嵌入式平台,您需要覆盖operator new和/或使用placement new进行内存分配。

答案 5 :(得分:14)

作为固件/嵌入式系统工程师,我可以告诉大家一些原因,为什么C仍然是C ++的第一选择,是的,我能熟练掌握它们。

1)我们开发的一些目标对于代码和数据都有64kB的RAM,所以你必须确保每个字节数,是的,我已经处理了代码优化以节省4个字节,花了我2​​个小时,那是在2008年。

2)在我们让它们进入最终代码之前,我们会检查每个C库函数,因为大小限制,所以我们更喜欢人们不使用除法(没有硬件分隔符,因此需要一个大型库),malloc(因为我们没有堆,所有内存都是从512字节块中的数据缓冲区分配的,并且必须经过代码审查),或者其他面向对象的实践带来很大的损失。请记住,您使用的每个库函数都算在内。

3)有没有听说过叠加这个词?你有这么少的代码空间,有时你必须用另一组代码交换东西。如果调用库函数,则库函数必须是常驻函数。如果只在覆盖函数中使用它,那么依赖于太多面向对象的方法会浪费大量空间。所以,不要假设任何C库函数,更不用说接受C ++了。

4)由于有限的硬件设计(即以某种方式连接的ECC引擎)或应对硬件错误,需要进行铸造和均匀打包(其中未对齐的数据结构跨越字边界)。你不能明确地假设太多,那么为什么要过多地反对呢?

5)最糟糕的情况:消除一些面向对象的方法会迫使开发在使用可能爆炸的资源(即在堆栈上而不是从数据缓冲区分配512字节)之前进行思考,并防止一些潜在的最坏没有测试或消除整个代码路径的案例场景。

6)我们使用大量抽象来保持硬件不受软件影响,并使代码尽可能便携,并且模拟友好。硬件访问必须包装在有条件地在不同平台之间编译的宏或内联函数中,数据类型必须作为字节大小而不是目标特定,不允许直接指针使用(因为某些平台假设内存映射I / O是等数据存储器)等。

我能想到更多,但你明白了。我们固件人员确实有面向对象的培训,但嵌入式系统的任务可以是硬件导向的,低级别的,它本质上不是高级别或可抽象的。

BTW,我所经历的每一项固件工作都使用源代码控制,我不知道你从哪里获得这个想法。

- 来自SanDisk的一些固件人。

答案 6 :(得分:10)

  

我的个人偏好是C,因为:

     
      
  • 我知道代码的每一行(和成本)
  •   
  • 我不太清楚C ++知道代码的每一行(和成本)
  •   

人们为什么这么说?除非检查asm输出,否则知道C的每一行都在做什么。 C ++也是如此。

例如,asm这个无辜的陈述产生了什么:

a[i] = b[j] * c[k];

它看起来相当无辜,但是基于gcc的编译器会为8位微处理器生成这个asm

CLRF 0x1f, ACCESS
RLCF 0xfdb, W, ACCESS
ANDLW 0xfe
RLCF 0x1f, F, ACCESS
MOVWF 0x1e, ACCESS
MOVLW 0xf9
MOVF 0xfdb, W, ACCESS
ADDWF 0x1e, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfa
MOVF 0xfdb, W, ACCESS
ADDWFC 0x1f, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0xfee, 0x1c
NOP
MOVFF 0xfef, 0x1d
NOP
MOVLW 0x1
CLRF 0x1b, ACCESS
RLCF 0xfdb, W, ACCESS
ANDLW 0xfe
RLCF 0x1b, F, ACCESS
MOVWF 0x1a, ACCESS
MOVLW 0xfb
MOVF 0xfdb, W, ACCESS
ADDWF 0x1a, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfc
MOVF 0xfdb, W, ACCESS
ADDWFC 0x1b, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0xfee, 0x18
NOP
MOVFF 0xfef, 0x19
NOP
MOVFF 0x18, 0x8
NOP
MOVFF 0x19, 0x9
NOP
MOVFF 0x1c, 0xd
NOP
MOVFF 0x1d, 0xe
NOP
CALL 0x2142, 0
NOP
MOVFF 0x6, 0x16
NOP
MOVFF 0x7, 0x17
NOP
CLRF 0x15, ACCESS
RLCF 0xfdf, W, ACCESS
ANDLW 0xfe
RLCF 0x15, F, ACCESS
MOVWF 0x14, ACCESS
MOVLW 0xfd
MOVF 0xfdb, W, ACCESS
ADDWF 0x14, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfe
MOVF 0xfdb, W, ACCESS
ADDWFC 0x15, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0x16, 0xfee
NOP
MOVFF 0x17, 0xfed
NOP

产生的指令数量主要取决于:

  • a,b和c的大小。
  • 这些指针是存储在堆栈上还是全局
  • i,j和k是在堆栈上还是全局

在微小的嵌入式世界中尤其如此,其中处理器只是没有设置来处理C.所以我的答案是C和C ++彼此一样糟糕,除非你总是检查asm输出,他们和对方一样好。

雨果

答案 7 :(得分:8)

我听说有些人更喜欢C用于嵌入式工作,因为它更简单,因此更容易预测将生成的实际代码。

我个人认为编写C风格的C ++(使用模板进行类型安全)会给你带来很多好处,但我看不出任何真正的理由。

答案 8 :(得分:7)

我认为没有理由使用C而不是C ++。无论你在C中做什么,你也可以用C ++做。如果您想避免VMT的开销,请不要使用虚方法和多态。

但是,C ++可以提供一些非常有用的习惯用语而不会产生任何开销。我最喜欢的一个是RAII。在内存或性能方面,类不是必需的昂贵......

答案 9 :(得分:6)

我在IAR Workbench上为ARM7嵌入式paltform编写了一些代码。我强烈建议依靠模板进行编译时优化和路径预测。避免像瘟疫一样动态铸造。根据Andrei Alexandrescu的书Modern C++ design中的规定,使用特征/政策对您有利。

我知道,这可能很难学,但我也相信你的产品会从这种方法中受益。

答案 10 :(得分:5)

一个很好的理由,有时唯一的原因是特定的嵌入式系统仍然没有C ++编译器。例如Microchip PIC微控制器就是这种情况。它们非常容易编写,并且它们有一个免费的C编译器(实际上是C的一个轻微变体),但是看不到C ++编译器。

答案 11 :(得分:5)

对于受限于4K ram的系统,我会使用C而不是C ++,这样你就可以确保看到正在发生的一切。使用C ++的事情是,使用更多的资源(包括CPU和内存)比看起来像浏览代码一样容易。 (哦,我只是创建另一个BlerfObject来做那个......呐喊!内存不足!)

你可以用C ++来做,如前所述(没有RTTI,没有vtable等等),但你会花费尽可能多的时间来确保你的C ++使用不会像你那样离开你相当于C。

答案 12 :(得分:4)

人类思维通过尽可能多地进行评估来处理复杂性,然后决定重点关注什么,以及丢弃或贬低其余部分。这是营销品牌背后的全部基础,主要是图标。

为了对抗这种趋势,我更喜欢C到C ++,因为它会迫使你思考你的代码,以及它如何更紧密地与硬件交互 - 无情地关闭。

从长期的经验来看,我相信C会迫使你想出更好的问题解决方案,部分原因是不要让你浪费大量的时间来满足一些编译器 - 作家的想法。一个好主意,或弄清楚“幕后”发生了什么。

在这种情况下,像C这样的低级语言让你花费大量时间专注于硬件并构建良好的数据结构/算法包,而高级语言让你花费大量时间搔头想知道会发生什么在那里,为什么你不能在你的特定环境和环境中做一些完全合理的事情。击败你的编译器(强类型是最糟糕的罪犯)并不能充分利用时间。

我可能很适合程序员模具 - 我喜欢控制。在我看来,这不是程序员的个性缺陷。控制是我们得到的报酬。更具体地说,FLAWLESSLY控制。 C为您提供比C ++更多的控制权。

答案 13 :(得分:3)

个人有4kb的内存我会说你没有从C ++中获得更多的里程数,所以选择一个似乎是最好的编译器/运行时组合的工作,因为语言可能不会太重要。

请注意,无论如何它也不仅仅是语言,因为图书馆也很重要。通常C libs的最小大小略小,但我可以想象一个针对嵌入式开发的C ++库被削减,所以一定要测试。

答案 14 :(得分:2)

C在可移植性方面取胜 - 因为它在语言规范方面不那么模糊;因此,在不同的编译器之间提供更好的可移植性和灵活性等(减少麻烦)。

如果您不打算利用C ++功能来满足需求,那么请使用C.

答案 15 :(得分:2)

有人说C编译器可以生成更高效的代码,因为它们不必支持高级C ++功能,因此可以更加积极地进行优化。

当然,在这种情况下,您可能希望将两个特定的编译器放在测试中。

答案 16 :(得分:2)

你有多少ROM / FLASH?

4kB的RAM仍然意味着存在数百千字节的FLASH来存储实际代码和静态数据。这个大小的RAM往往仅仅意味着变量,如果你小心那些你可以在代码行的内存中安装相当大的程序。

然而,由于对象的运行时构造规则,C ++倾向于使代码和数据在FLASH中更加困难。在C中,常量结构可以很容易地放入FLASH存储器中并作为硬件常量对象访问。在C ++中,一个常量对象需要编译器在编译时评估构造函数,我认为这仍然超出了C ++编译器的作用(理论上,你可以做到,但实际上很难做到) 。

所以在一个“小内存”,“大型FLASH”环境中,我会随时随地使用C语言。请注意,一个很好的中间选择i C99,它具有非基于类的代码的大部分优秀C ++特性。

答案 17 :(得分:2)

我的选择通常取决于我们决定使用的C库,它是根据设备需要做的选择的。所以,9/10次......它最终成为uclibc或newlib和C.我们使用的内核对此也有很大影响,或者如果我们正在编写自己的内核。

它也是共同点的选择。大多数优秀的C程序员使用C ++都没有问题(尽管许多人一直抱怨他们使用它)..但我没有发现反过来是真的(根据我的经验)。

在我们正在开发的项目上(涉及一个基础内核),大多数事情都是在C语言中完成的,但是在C ++中实现了一个小型网络堆栈,因为使用C ++实现网络更简单,更少问题

最终结果是,设备将工作并通过验收测试,否则将不会。如果你可以使用语言z在xx堆栈和yy堆约束中实现foo,那就去吧,使用任何能让你提高效率的东西。

我的个人偏好是C,因为:

  • 我知道代码的每一行(和成本)
  • 我不太清楚C ++知道代码的每一行(和成本)

是的,我对C ++感到满意,但我不知道它和标准C一样。

现在,如果你可以反过来说,那么,使用你所知道的:)如果它有效,通过测试等等。问题是什么?

答案 18 :(得分:2)

  

在开发非常有限的时候,你是否认为有任何理由坚持使用C89?   硬件(4kb的RAM)?

就个人而言,当谈到嵌入式应用程序时(当我说嵌入式应用程序时,我并不是指winCE,iPhone等等。今天臃肿的嵌入式设备)。我指的是资源有限的设备。 我更喜欢C,尽管我也使用过C ++。

例如,您正在谈论的设备具有 4kb 的RAM,正因为这个原因我不会考虑C ++。当然,您可以使用C ++设计一些小的东西,并限制您在应用程序中使用它,就像其他帖子所建议的那样,但C ++“可能”最终可能使您的应用程序变得复杂/膨胀。

你要静态链接吗?您可能希望使用c ++和c比较静态虚拟应用程序。这可能会导致您考虑使用C语言。另一方面,如果您能够在内存需求中构建C ++应用程序,那就去做吧。

IMHO, 通常,在嵌入式应用程序中,我喜欢了解正在发生的一切。谁在使用内存/系统资源,多少以及为什么?他们什么时候解放他们?

为具有X资源,cpu,内存等的目标开发时。我试图保持在使用这些资源的较低方面,因为你永远不知道将来会有什么要求,因此你需要添加更多代码这个项目“应该”是一个简单的小应用程序,但最终会变得更大。

答案 19 :(得分:1)

更喜欢C恕我直言的唯一理由是,如果您的平台的C ++编译器状态不佳(错误,优化不佳等)。

答案 20 :(得分:1)

本书C++ for Game Programmers包含有关何时根据C ++中的功能增加代码大小的信息。

答案 21 :(得分:1)

你在C99中内联。也许你喜欢ctors,但是让dtors正确的事情可能会很混乱。如果剩下的唯一不使用C的原因是名称空间,我真的会坚持使用C89。这是因为您可能希望将其移植到略有不同的嵌入式平台。您可能稍后开始用相同的代码编写C ++。但要注意以下几点,其中C ++不是C的超集。我知道你说你有一个C89编译器,但无论如何都要与C99进行C ++比较,因为例如K&amp; R之后任何C的第一项都是正确的。< / p>

sizeof'a'&gt;在C中为1,而不是在C ++中。 在C中,您有VLA可变长度数组。示例: func(int i){int a [i] 。 在C中,您有VAM变量数组成员。示例: struct {int b; int m [];}

答案 22 :(得分:1)

一般没有。 C ++是C的超级集合。对于新项目尤其如此。

你在避免使用Cpu构造方面走在正确的轨道上,这些构造在cpu时间和内存占用方面可能很昂贵。

请注意,像多态这样的东西可能非常有价值 - 本质上是函数指针。如果您发现需要它们,请明智地使用它们。

此外,良好(精心设计的)异常处理可以使您的嵌入式应用程序比使用传统错误代码处理事物的应用程序更可靠。

答案 23 :(得分:1)

只想说没有&#34; UNLIMITED&#34;资源。这个世界上的一切都是有限的,每个应用程序都应该考虑资源使用,无论是ASM,C,JAVA还是JavaScript。分配几个Mbs的假人&#34;只是为了确保&#34;使iPhone 7,Pixel和其他设备非常轻松。无论你是4kb还是40Gb。

但另一方面反对资源浪费 - 是节省这些资源所需的时间。如果需要额外花费1周的时间在C中编写一个简单的东西来节省一些滴答和几个字节而不是使用已经实现,测试和分发的C ++。何必?它就像购买USB集线器一样。是的,你可以自己做,但它会变得更好吗?更可靠?如果算上你的时间会更便宜吗?

只是一个想法 - 即使您的插座供电也不是无限的。尝试研究它的来源,你会发现它主要来自燃烧的东西。能量和物质定律仍然有效:没有物质或能量出现或消失,而是变换。

答案 24 :(得分:0)

这取决于编译器。

并非所有的嵌入式编译器都实现了所有的C ++,即使他们这样做,也可能不擅长避免代码膨胀(这对模板来说总是存在风险)。用一些较小的程序测试它,看看你是否遇到任何问题。

但鉴于良好的编译器,不,没有理由不使用C ++。

答案 25 :(得分:0)

我刚刚找到了一个如何使用ISO C ++进行嵌入式开发的示例,这对于每当使用C ++或C时做出决定的人来说都很有趣。

由Bjarne Stroustrup提供at his homepage

要了解ISO C ++如何用于严格的嵌入式系统编程,请参阅JSF air vehicle C++ coding standards

答案 26 :(得分:0)

对问题的不同方面的不同回答:

“的malloc”

之前的一些回复谈了很多。为什么你甚至认为这个电话存在?对于一个真正的小平台,malloc往往不可用,或者绝对是可选的。当您在系统底部安装RTOS时,实现动态内存分配往往是有意义的 - 但在此之前,它纯粹是危险的。

没有它,你可以走得很远。想想所有旧的FORTRAN程序,它们甚至没有适当的局部变量堆栈......

答案 27 :(得分:0)

对于内存分配问题,我建议使用Quantum Platform及其状态机方法,因为它会在初始化时分配您需要的所有内容。它还有助于缓解争用问题。

此产品在C和C ++上运行。

答案 28 :(得分:0)

全球有许多不同的控制器制造商,当您研究他们的设计和需要用来配置的指令集时,您可能会遇到很多麻烦。汇编语言的主要缺点是依赖于机器/体系结构。对于开发人员来说,要认真完成那里列出的所有指令以完成针对不同控制器的编码,这确实是一个巨大的要求。这就是为什么C在嵌入式开发中变得越来越流行的原因,因为C足够高级,可以从与硬件相关的细节中抽象出算法和数据结构,从而使源代码可跨多种目标硬件,与体系结构无关的语言进行移植,并且非常容易转换并维护代码。但是我们确实看到诸如C,C ++,Python,Java等高级语言(面向对象)正在不断发展,足以使它们成为嵌入式系统开发的雷达。

答案 29 :(得分:0)

我建议使用C ++,但要有限制和注意事项。

  1. 上市时间和可维护性。 C ++开发更容易,更快捷。因此,如果您正处于设计阶段,请选择足以使用C ++的控制器。 (请注意,某些大批量市场需要尽可能低的成本,而您在这种情况下无法做出选择。)

  2. 速度。。C的速度可能比C ++快,但是请确保这不会增加速度。因此,您可以使用C ++。开发算法,对其进行测试,并仅在需要时才使它们更快(!)。使用探查器,指出瓶颈并以 extern“ C” 方式重写瓶颈,以达到C速度。 (如果仍然很慢地在ASM中实现该部分)

  3. 二进制大小。。C ++代码较大,但是这里有一个great answer,用于说明详细信息。无论使用C还是C ++编译器编译,给定C代码的已编译二进制文件的大小都将相同。 “可执行文件的大小与语言几乎无关,而与项目中包含的库有关。” 使用C ++,但避免使用高级功能,例如streamsstringnewvirtual函数等。由于大小限制(基于this答案),请先检查所有库函数,然后再将它们放入最终代码中

答案 30 :(得分:-5)

在这样一个有限的系统上。只是去Assembler。使您可以完全控制每个方面,并且不会产生任何开销。

由于很多嵌入式编译器并不是最好的优化器(特别是如果将它与最先进的编译器(如我们用于桌面的那些编译器)(英特尔,视觉工作室等)进行比较),可能要快得多。

<是>“是的,但是......但是c可以重复使用......”。在这样一个有限的系统上,你很可能无论如何都不会在不同的系统上重复使用大部分代码。在同一个系统上,汇编程序就像可重用一样。

相关问题