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

Posted

技术标签:

【中文标题】是否有任何理由使用 C 而不是 C++ 进行嵌入式开发? [关闭]【英文标题】:Is there any reason to use C instead of C++ for embedded development? [closed] 【发布时间】:2010-10-23 04:47:45 【问题描述】:

问题

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

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

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

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

结论

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

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

    用 C 语言预测实际代码更容易,如果您只有 4kb 的内存,这一点非常重要。 我的团队主要由 C 开发人员组成,因此不会经常使用高级 C++ 功能。 我找到了一种在我的 C 编译器 (C89) 中内联函数的方法。

您提供了这么多好的答案,很难接受一个答案。 不幸的是,我无法创建一个 wiki 并接受它,所以我会选择一个让我思考最多的答案。

【问题讨论】:

有一件事:永远清楚你在用哪种语言编写。不要试图用“C/C++”编写程序。用 C 编写,或用 C++ 编写,知道您将使用哪些语言功能,哪些不会。 另见***.com/questions/649789/… 定义“嵌入式开发” @DavidThornley,您可能很适合嵌入式案例,但我非常惊喜地发现混合 C 和 C++ 代码可以很好地协同工作,我希望扩展常见的行业开源应用程序,例如Kamailio 与 STL。我正式鼓励这种 STL 和 C 代码的使用,因为它提供了巨大的功能和易于维护,同时产生几乎零问题(C++ 中缺乏嵌入式结构是对 C++ 的可怕罪行,应该尽快纠正)。 深思熟虑,这是一篇很棒的文章,ZeroMQ 的设计者和作者讨论了为什么他后悔用 C++ 而不是 C 编写代码库。这完全不是我所期望的,并且原因在其他地方找不到在本页。 250bpm.com/blog:4 【参考方案1】:

我推荐带有限制和注释的 C++。

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

    速度。 C 可以比 C++ 更快,但请确保速度增益不大。因此,您可以使用 C++。开发您的算法,测试它们,并仅在需要时使它们更快(!)。使用分析器,指出瓶颈并以 extern "C" 方式重写它们,以实现 C 速度。 (如果在 ASM 中实现该部分仍然很慢)

    二进制大小。 C++ 代码更大,但这里有一个great answer 说明详细信息。无论是使用 C 编译器还是 C++ 编译器编译,给定 C 代码的编译二进制文件的大小都是相同的。 “可执行文件的大小几乎与语言无关,而是与您项目中包含的库有关。” 使用 C++,但避免使用高级功能,例如 streamsstringnewvirtual 函数等。由于大小限制(基于this 答案)

    ,在将它们放入最终代码之前检查所有库函数

【讨论】:

【参考方案2】:

全球有许多不同的控制器制造商,当您查看他们的设计和需要用于配置的指令集时,您可能会遇到很多麻烦。汇编语言的主要缺点是依赖于机器/架构。对于开发人员来说,为了完成不同控制器的编码而将所有说明都牢记在心,这确实是一个巨大的要求。这就是为什么 C 在嵌入式开发中变得更受欢迎的原因,因为 C 足够高级,可以从依赖于硬件的细节中抽象出算法和数据结构,使源代码可跨各种目标硬件、体系结构独立的语言移植,并且非常容易转换和维护代码。但我们确实看到一些高级语言(面向对象),如 C、C++、Python、Java 等,正在不断发展,足以让它们受到嵌入式系统开发的关注。

【讨论】:

【参考方案3】:

人类的大脑通过尽可能多地评估来处理复杂性,然后决定关注什么是重要的,然后丢弃或贬低其余的。这是市场营销中品牌化背后的全部基础,主要是图标。

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

从长期的经验来看,我相信 C 会迫使你想出更好的问题解决方案,部分原因是不妨碍你,而不是强迫你浪费大量时间来满足一些编译器编写者认为的约束一个好主意,或者弄清楚“幕后”发生了什么。

在这种情况下,像 C 这样的低级语言让您花费大量时间专注于硬件和构建良好的数据结构/算法包,而高级语言则让您花费大量时间摸不着头脑想知道发生了什么在那里,为什么你不能在你的特定环境和环境中做一些完全合理的事情。打败你的编译器(强类型是最严重的违规者)并不是对时间的有效利用。

我可能很适合程序员的模式——我喜欢控制。在我看来,这不是程序员的人格缺陷。控制是我们得到报酬的事情。更具体地说,完美的控制。 C 比 C++ 给你更多的控制权。

【讨论】:

Martin Sistrik,ZeroMQ 的作者,在讨论他为什么希望现在用 C 而不是 C++ 编写 ZeroMQ 时提出了几乎相同的观点。看看250bpm.com/blog:8【参考方案4】:

只想说没有“无限”资源的系统。这个世界上的一切都是有限的,每个应用程序都应该考虑资源使用,无论是 ASM、C、JAVA 还是 javascript。分配几 Mbs “只是为了确定” 的假人使 iPhone 7、Pixel 和其他设备非常笨拙。无论您有 4kb 还是 40 Gb。

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

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

【讨论】:

【参考方案5】:

使用 C 而不是 C++ 的两个原因:

    对于很多嵌入式处理器来说,要么没有 C++ 编译器,要么就得额外付费。 我的经验是,相当大比例的嵌入式软件工程师很少或根本没有 C++ 经验——要么是因为 (1),要么是因为它往往不教授电子工程学位 - 所以这样会更好坚持他们所知道的。

此外,原始问题和一些 cmets 提到了 4 Kb 的 RAM。对于典型的嵌入式处理器,RAM 的数量(大部分)与代码大小无关,因为代码是从闪存​​存储和运行的。

当然,需要牢记代码存储空间的数量,但随着新的、容量更大的处理器出现在市场上,除了对成本最敏感的项目之外,它不再像过去那样成为问题.

关于将 C++ 的子集用于嵌入式系统:现在有一个 MISRA C++ 标准,可能值得一看。

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

【讨论】:

请看下面我的较长回复:C++ 往往会使将常量数据放入 FLASH 中变得非常困难。 使用 C 而不是 C++ 的潜在好理由是 C 的标准 ABI。只是为了完整性。【参考方案6】:

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

我知道每一行代码在做什么(以及成本) 我对 C++ 的了解不够深入,无法了解每一行代码的作用(以及成本)

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

例如,这个无害的语句产生什么 asm:

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

它看起来很无辜,但是基于 gcc 的编译器为 8 位 micro 生成了这个 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 输出,在在这种情况下,它们彼此一样好。

雨果

【讨论】:

还要注意,在所有实际调用乘法函数的中间有一条调用指令。所有这些代码甚至都不是乘法指令! 熟悉 micro 的人通常会知道一种简单的方法来单独处理 C 代码的每个部分,一个好的编译器不应该生成比这更糟糕的代码。可以有效处理上述表达式的唯一方法是做出可能不适合 C 编译器的假设。 这看起来像是用于 PIC18 的 Microchip XC8 编译器的免费版的输出。我相信这个编译器故意发出臃肿的代码来鼓励人们购买他们的 pro 版本。我上次使用它已经有几年了,所以我不知道它的行为是否仍然相同。【参考方案7】:

作为一名固件/嵌入式系统工程师,我可以告诉你们为什么 C 仍然是 C++ 的第一选择的一些原因,是的,我精通这两种语言。

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

2) 每个 C 库函数在我们将它们放入最终代码之前都经过审查,因为大小限制,所以我们不希望人们使用除法器(没有硬件除法器,所以需要一个大库),malloc(因为我们没有堆,所有内存都是从 512 字节块中的数据缓冲区分配的,并且必须进行代码审查),或者其他带有大量惩罚的面向对象实践。请记住,您使用的每个库函数都会计数。

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

4) 由于有限的硬件设计(即以某种方式连接的 ECC 引擎)或应对硬件错误,需要强制转换甚至打包(未对齐的数据结构跨越字边界)。你不能隐含地假设太多,那么为什么要过多地面向对象呢?

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

6) 我们确实使用了大量抽象来将硬件与软件隔离开来,并使代码尽可能可移植,并且模拟友好。硬件访问必须包装在宏或内联函数中,这些函数在不同平台之间有条件地编译,数据类型必须转换为字节大小而不是特定于目标,不允许直接使用指针(因为某些平台假设内存映射 I/O 是与数据存储器相同)等。

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

顺便说一句,我从事的每个固件工作都使用源代码控制,我不知道你从哪里得到这个想法。

-SanDisk 的一些固件专家。

【讨论】:

早在 90 年代初期,覆盖是非常流行的技术(至少在 DOS 世界中) 好点盛。在功能有限且资源更加有限的项目中,C++ 感觉就像是电话亭中的相扑选手。 我认为这个答案非常主观,并没有提供具体的推理。 C++ 并不必然表示“面向对象”。 嵌入式系统的任务本质上是不可抽象的,这根本不是真的。您在第 6 点中自己说过:“我们确实使用了很多抽象来使 hw 远离 sw 并使代码尽可能可移植”:-) BTW:“抽象”并不一定意味着“多态”。【参考方案8】:

在这样一个有限的系统上。就去汇编吧。让您完全控制各个方面,并且不会产生任何开销。

可能也快很多,因为许多嵌入式编译器并不是最好的优化器(尤其是如果将其与我们的桌面编译器(英特尔、Visual Studio 等)等最先进的编译器进行比较)

“是的,是的……但是 c 是可重复使用的并且……”。在这样一个有限的系统上,您可能不会在不同的系统上重用大部分代码。在同一系统上,汇编器同样可重用。

【讨论】:

在这里使用汇编程序会很复杂。特别是当我需要支持 micro SD、智能卡和 zigbee 进行无线通信时。 :( 简而言之,你可以花一年的时间用 C 写代码,或者用 3 年的时间用汇编写代码。然后,当您使用不同的处理器开始一个新项目时,您可以花 6 个月的时间用 C 语言添加新功能,或者用 4 年的时间用汇编语言重写它。 就像您在 4 年后仍然使用相同的程序一样。无论如何,您可能已经重写了大部分内容。【参考方案9】:

针对问题的不同方面的不同答案帖子:

“malloc”

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

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

【讨论】:

【参考方案10】:

你有多少 ROM/FLASH?

4kB 的 RAM 仍然意味着有数百 KB 的 FLASH 来存储实际代码和静态数据。这种大小的 RAM 往往仅用于变量,如果您对这些变量小心,您可以将相当大的程序(就代码行而言)放入内存中。

但是,由于对象的运行时构造规则,C++ 倾向于使将代码和数据放入 FLASH 中变得更加困难。在 C 语言中,可以轻松地将常量结构放入 FLASH 内存并作为硬件常量对象进行访问。在 C++ 中,常量对象需要编译器在编译时评估构造函数,我认为这仍然超出了 C++ 编译器的能力(理论上,你可以做到,但实际上很难做到) .

因此,在“小型 RAM”、“大型 FLASH”这种环境中,我随时都会使用 C。请注意,C99 是一个很好的中间选择,它具有非基于类的代码的大部分优秀 C++ 特性。

【讨论】:

有什么理由为什么将在 C 中放入闪存中的相同结构在 C++ 中也不会最终放入 Flash 中?您没有必须在 C++ 中向您的结构添加构造函数。【参考方案11】:

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

这也是一种共同点的选择。大多数优秀的 C 程序员在使用 C++ 时都没有问题(尽管许多人在使用 C++ 的过程中一直抱怨).. 但我还没有发现相反的情况(根据我的经验)。

在我们正在进行的一个项目中(涉及一个全新的内核),大多数事情都是用 C 完成的,但是一个小的网络堆栈是用 C++ 实现的,因为使用 C++ 实现网络更容易且问题更少.

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

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

我知道每一行代码在做什么(以及成本) 我对 C++ 的了解不够深入,无法了解每一行代码的作用(以及成本)

是的,我对 C++ 很熟悉,但我对它的了解不如标准 C。

现在,如果您可以反过来说,好吧,使用您所知道的 :) 如果它有效,通过测试等.. 有什么问题?

【讨论】:

> # 我知道每一行代码在做什么(和成本) 编写了编译器后,我不能确定...一个好的 C 编译器可以对你的代码做一些令人惊讶的事情因为它对事物有一个很好的全局概述。它不会逐行编译。 @jakobengblom2:对于嵌入式开发,始终如一的性能通常比获得最高性能更重要。如果试图确定一段代码是否满足时序要求,让编译器采用可用于“测试”固件的优化,但在实际固件中不起作用。【参考方案12】:

这取决于编译器。

并非所有嵌入式编译器都实现了所有 C++,即使实现了,它们也可能不擅长避免代码膨胀(模板总是存在风险)。用几个小程序测试一下,看看有没有问题。

但是考虑到一个好的编译器,不,没有理由不使用 C++。

【讨论】:

【参考方案13】:

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

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

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

不过,您的朋友是链接器映射:经常检查它,您会很快发现代码源和静态内存膨胀。

之后,标准的动态内存使用注意事项适用:在您提到的受限环境中,您可能根本不想使用动态分配。有时您可以摆脱用于小型动态分配的内存池,或“基于帧”的分配,您预先分配一个块并在以后丢弃整个事情。

【讨论】:

【参考方案14】:

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

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

【讨论】:

没有。我的意思是在 C 中你有 (sizeof 'a') == sizeof (int)。在 C++ 中,你有 1 == sizeof 'a' 更不用说“int * a; ...;a = (int *)malloc(size * sizeof(int));”是在 C 和 C++ 中工作的分配内存的方法,不应该在这两种情况下使用。使用“a = malloc(size * sizeof(int));”或“矢量 a(大小);”甚至“int * a = new int[size];”而是。 我不明白你对 dtor 的看法。关于它们的全部意义在于它们使您的其余代码更少变得混乱。 +1,不知道为什么这篇文章得到如此糟糕的评价。但我同意 jalf 的观点,当使用正确的 (RAII) 方式时,析构函数 simplify 代码非常强大。 (你可以说他们“在幕后工作”,但他们做正确的代码无论如何都要手动做的事情。) 我认为我指出的内容与问题非常相关。我也坚持我的说法,即 dtors 可能很困难,原因正是它自动发生。我得到了减分——那真的很难。猜猜这是因为我没有说“是的,去 C++ 它很棒”。【参考方案15】:

对于 4kb 内存的个人而言,我会说您并没有从 C++ 中获得更多的好处,所以只需选择一个看起来是该工作的最佳编译器/运行时组合的那个,因为语言可能并不重要.

请注意,这也不仅仅与语言有关,因为库也很重要。通常 C 库的最小大小略小,但我可以想象针对嵌入式开发的 C++ 库被削减了,所以一定要测试。

【讨论】:

【参考方案16】:

对于一个非常资源受限的目标,例如 4KB 的 RAM,我会先用一些样本试水,然后再投入大量的努力,这些努力无法轻易移植回纯 ANSI C执行。

嵌入式 C++ 工作组确实提出了该语言的标准子集和标准库的标准子集以与之配套。不幸的是,当 C User's Journal 死了时,我忘记了这项工作。 Wikipedia好像有文章,committee还在。

在嵌入式环境中,您确实必须小心内存分配。要强制执行该注意,您可能需要将全局 operator new() 及其朋友定义为甚至无法链接的东西,以便您知道它没有被使用。另一方面,当与稳定、线程安全和延迟保证的分配方案一起明智地使用时,位置 new 可能会成为您的朋友。

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

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

另一个可能出现的问题是与您的调试器的兼容性。其他可用的硬件调试器对与原始源代码交互的支持非常有限,这并不罕见。如果您实际上必须在汇编中进行调试,那么有趣的 C++ 名称修饰可能会给任务增加额外的混乱。

RTTI、动态转换、多重继承、重多态性和异常都伴随着使用它们的一些运行时成本。如果使用这些功能,其中一些功能会花费整个程序的成本,其他功能只会增加需要它们的类的权重。了解差异,明智地选择高级功能,至少了解粗略的成本/收益分析。

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

此外,在嵌入式环境中,通常需要先初始化硬件设备才能使用它们,如果没有操作系统和引导加载程序,那么就是您的代码执行此操作。您需要记住,全局对象的构造函数在之前运行main(),因此您需要修改本地 CRT0.S(或其等效项)以完成硬件初始化在调用全局构造函数之前。显然,main() 的顶部已经来不及了。

【讨论】:

+1,很好的答案。但我认为你真正需要担心的唯一模板实例化是(相对罕见的)递归类型——对于“常规”非递归类型,实例化相当于你手动输入的代码。 @j_random_hacker,是的。但是,当出现第二个(或第三个)实例化时,模板的习惯可能会导致偶然的意外,而在使用点的正确类型强制可能会阻止它。这只是需要注意的事情。 @RBerteig:很好,模板允许更少的类型强制可能性 => 可能比使用非模板代码产生更多不同的实例化。【参考方案17】:

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

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

例如,您所说的设备具有 4kb 的 RAM,正因为如此,我不会考虑使用 C++。当然,您也许可以使用 C++ 设计一些小的东西,并像其他帖子所建议的那样限制您在应用程序中的使用,但 C++“可能”最终使您的应用程序在幕后变得复杂/臃肿。

你要静态链接吗?您可能想比较使用 c++ 与 c 的静态虚拟应用程序。这可能会导致您改为考虑 C。另一方面,如果您能够在内存需求范围内构建 C++ 应用程序,那就去做吧。

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

在为具有 X 数量的资源、cpu、内存等的目标进行开发时。我尽量保持在使用这些资源的较低端,因为您永远不知道未来会出现什么需求,因此您需要添加更多代码该项目“本应”是一个简单的小型应用程序,但最终变得更大。

【讨论】:

【参考方案18】:

对于受限于 4K 内存的系统,我会使用 C 而不是 C++,这样您就可以确保看到正在发生的一切。 C++ 的问题在于,它使用的资源(CPU 和内存)比看代码要容易得多。 (哦,我将创建另一个 BlerfObject 来执行此操作……哎呀!内存不足!)

您可以在 C++ 中执行此操作,如前所述(没有 RTTI、没有 vtables 等),但您将花费尽可能多的时间来确保您的 C++ 使用不会像您在执行C中的等价物。

【讨论】:

您的最后一句话是正确的,但无关紧要,因为 C++ 提供了优于 C 的其他优势,这(可能)打破了平衡。 Piotr 已经提到了其中一些(零成本)优势。【参考方案19】:

C 在可移植性方面胜出——因为它在语言规范中不那么模棱两可;因此在不同的编译器等方面提供了更好的可移植性和灵活性(减少了麻烦)。

如果您不打算利用 C++ 功能来满足需求,请选择 C。

【讨论】:

语言是否明确取决于人们是否认为它指定了过去被认为是常识的事物,但现在不是[例如。即使乘积超过 2147483647,32 位静默环绕二进制补码硬件的编译器也应将 unsigned mul(unsigned short x, unsigned short y) return x*y; 之类的东西处理为没有副作用,或者它应该将 void get_float_bits(float *fp, uint32_t n) *(uint32_t)fp = n; 视为可能改变 @ 的值987654323@].【参考方案20】:

恕我直言,偏爱 C 的唯一原因是您平台的 C++ 编译器状况不佳(错误、优化不佳等)。

【讨论】:

内存/资源利用率如何? 怎么样? C++ 编译器没有理由生成比 C 编译器效率低的代码,除非代码使用 RTTI,而在嵌入式系统上没有人这样做。【参考方案21】:

我在 IAR Workbench 上为 ARM7 嵌入式平台编写了一些代码。我强烈建议依靠模板进行编译时优化和路径预测。避免像瘟疫一样的动态投射。按照 Andrei Alexandrescu 的书Modern C++ design 中的规定,利用特征/策略来发挥自己的优势。

我知道,这可能很难学习,但我也确信您的产品将从这种方法中受益。

【讨论】:

【参考方案22】:

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

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

【讨论】:

相关:据我所知,restrict 关键字是 C++(也是 C++11)中唯一缺少的与优化相关的 C 构造。【参考方案23】:

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

本产品可在 C 和 C++ 上运行。

【讨论】:

【参考方案24】:

我建议使用 C++ 编译器,但限制您使用 C++ 特定功能。您可以在 C++ 中像 C 一样编程(在使用 C++ 时包含 C 运行时,尽管在大多数嵌入式应用程序中您无论如何都不会使用标准库)。

你可以继续使用 C++ 类等,只是

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

【讨论】:

当然,如果你已经基本在写C了,不妨把它官方化。 为什么限制模板的使用?我认为模板函数在嵌入式系统中可能非常有用,例如展开循环。 您仍然可以使用模板,但我会非常小心使用它们,因为它们会迅速增加输出二进制文件的大小。当然,如果您的代码直接从 ROM 或类似程序运行,并且您有 ROM 空间可用,那么当然可以,但除此之外,您需要小心处理模板的操作(每个模板实例基本上都是再次复制的所有模板化代码在最坏情况下的最终可执行文件中)。【参考方案25】:

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

【讨论】:

Comeau Computing (comeaucomputing.com) 出售可编译为 C 的 C++ 编译器。 @shoosh:是的,网站设计很糟糕。然而,编译器本身被认为是该领域的领导者,至少在标准一致性方面(我没有关于性能的信息)。【参考方案26】:

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

但是,C++ 可以提​​供一些非常有用的习语,而无需额外开销。我的最爱之一是 RAII。就内存或性能而言,类不一定很昂贵...

【讨论】:

【参考方案27】:

一般不会。 C++ 是 C 的超集。对于新项目尤其如此。

您在避免 C++ 构造方面走在正确的道路上,因为 C++ 构造在 CPU 时间和内存占用方面可能会很昂贵。

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

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

【讨论】:

严格来说,C++ 不是 C 的严格超集,但在这种情况下,具体细节并不是特别重要。【参考方案28】:

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

【讨论】:

【参考方案29】:

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

我个人认为编写 C 风格的 C++(使用模板实现类型安全)会给您带来很多好处,但我看不出有什么真正的理由不这样做。

【讨论】:

以上是关于是否有任何理由使用 C 而不是 C++ 进行嵌入式开发? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

是否有任何理由应该将 C++ 11+ std::mutex 声明为全局变量,而不是作为函数参数传递给 std::thread ?

是否有任何理由更喜欢在 IDE 中运行应用程序而不是运行独立的可执行文件?

是否有任何理由使用一个 DataContext 实例,而不是几个?

嵌入式 Linux 中的 C 与 C++

是否有任何理由使用 (nr & 1 == 0) 而不是 (nr % 2 == 0) 来检查奇偶校验?

是否有任何理由在 grails 中使用通过 TagLibs 公开给定库的 JavaScript 库插件,而不是直接使用它?