在嵌入式Linux中,C与C++的区别

16

我正在开发一个嵌入式Linux(ARM)应用程序,每秒将执行500次,因此速度非常重要。我更喜欢使用C ++,但我担心即使避免使用虚函数等高级特性,它仍然比C慢。

是否有理由使用C,还是使用C ++编写同样可以呢?


8
虚函数不是你应该避免的东西。手动在C中实现相同功能的速度不会比编译器生成的版本更快,而且当不需要时,C++编译器很擅长将其优化掉。 - Martin York
1
@Martin是正确的。虚函数只需要一两条指令来调用,如果这确实是您需要的,那么在C中您也必须这样做。 - Mike Dunlavey
这里有一些有趣的相关阅读:https://dev59.com/oXI-5IYBdhLWcg3wCzxn#2039645 - Emile Cormier
2
尝试使用C和C++,然后在嵌入式设备上测量生成的二进制文件。如果您不想编写两个应用程序版本,只需使用您更熟悉的语言(C ++)即可。 - pmg
8个回答

18

C++通常不会比C语言在运行时产生额外的开销,除了一些像RTTI这样的特殊情况。

除非是一些奇怪的情况,否则编译器应该能够在编译时确定要调用哪个虚函数,因此不会增加任何额外的开销。

编辑:好的,由于存在各种编译器、CPU、运行库和操作系统,C++的某些特性可能会导致代码变慢,而其他特性则可能会导致代码变快。

但我们是否都同意,现在 C++ 不再自动排除嵌入式使用?


1
如果你把C++当作“丑化的C”(到处都是额外的强制类型转换),那么你所说的就是正确的。但是一旦你开始使用任何使C++“舒适”的东西,就会有更多无法优化的开销。 - R.. GitHub STOP HELPING ICE
2
虚函数和模板应该是零成本的。如果在任何合适的编译器上没有调用,则异常是零成本的。dynamic_cast通常可以在没有RTTI的情况下工作,但会有一些警告。代码大小可能是一个问题 - 但这当然取决于你对嵌入式的定义。 - Martin Beckett
2
当您可以从正常代码流中删除C错误处理时,异常会产生负面成本。优化器可以轻松地针对非异常情况进行优化,但是它们不知道C函数在出现错误时返回0、1还是-1。 - MSalters
我在一个只有32K闪存的嵌入式ARM上使用C++11和元编程!我发现,如果你使用现代编译器(我使用GCC 4.6),你可以将C代码移植到C++中,并且有时会看到大小减小。constexpr是一个重要的工具。 - odinthenerd
如果你的代码中有任何重要的数据结构,那么使用模板实现的STL容器实际上是一个巨大的优势。这些实现和你自己编写的实现一样好,甚至可能更好。如果需要,你还可以进行池分配。 - marko
@MSalters +1。当然,通常会将其与在C中不编写错误处理代码进行比较,而不是正确(并经过测试?是的...对的)的错误处理。RAII在正确性和可验证性方面是一个巨大的胜利。 - marko

8
在C++中,你有像模板元编程这样的东西,在其中解决了在C或任何其他过程式编程语言中需要在运行时执行的几种情况。
我应该多说一些。模板元编程和一些类继承技巧真的很神奇。它可以节省你大量的处理时间,否则你会通过“if”和“switch”来花费这些时间。
这意味着如果操作得当,C++实际上可能比C更快。
显然,你可以使用C++编写“C”的代码,而不会受到任何惩罚。 如果你不太喜欢C ++,我建议你使用“C on C ++”或“C with C ++ extensions”,以利用C ++的改进,但你真正的优势是通过以C ++方式编程。在那里,你会发现C ++通常比C更快或更干净,或者至少与C一样快。
不要害怕。面对C ++。 在stdc++(针对libc)之后,代码大小几乎没有任何开销。如果你的应用程序大小从中等到高,它将被稀释。
我从简单的8位ATmega到Marvell的ARM9,经过AVR32 UC3和Cortex-M3,始终发现使用C ++是有益的。
如果你需要在特定情况下获得具体的建议,请随时问我。

1
这正是我所期望的答案。即使是像在C中一样需要在块的开头声明var,C++编写起来也更有趣。当然,我更喜欢类封装而不是C全局函数。我希望避免使用动态内存分配或任何奇特的东西。 - Gregory Khrapunovich
1
C++在嵌入式领域的优势是非常广泛的。我发现__attribute((always_inline))在将抽象接口编译成无形物并仅留下对重要内容进行更强静态检查时,是一种救星。 - odinthenerd

7
选择C语言而非C++的主要原因是编译后二进制文件的大小,在嵌入式系统中这可能会成为真正的限制。
在性能方面,如果正确使用语言,两者之间没有明显的差异。只要你了解你所写代码的底层机制,写出慢速的C代码和慢速的C++代码同样容易。

6
只要C版本在功能上等同于C++版本,它们的大小差别可以忽略不计。例如,如果C++版本使用了继承,那么C版本必须编写相等的代码,不能取巧。在我的经验中,大小不是问题。 - Thomas Matthews
4
@Thomas:这不是事实,因为许多库代码会被引入并具有大量超出所需功能的子集,链接器无法确定哪些部分可以省略。使用任何合理的 C 库(如任何 BSD、uClibc、Bionic 等,只要不是将 stdio 写成 C++ 的 glibc)静态链接一个 C hello world 程序,然后再比较一个使用 iostream 的等效静态链接的 C++ 程序即可。 - R.. GitHub STOP HELPING ICE
2
@R..:如果您选择适当的libc实现,那么您应该公平地选择同样适当的iostream。Dietmar Kuehl在大约10年前证明了Hello World在C++中可以更小。简单的原因是链接器可以消除operator<<ostream&, float)但不能从printf()实现中消除case 'f': - MSalters
1
图书馆的大小(理论上)在某些环境中可能是一个问题,但原始问题提到了嵌入式Linux,因此它至少是32位系统,操作系统本身足够大。所以我怀疑在这种情况下大小并不重要 :) - user396672
我目前正在构建一个基于嵌入式Linux的系统,我选择为设备添加更多的闪存,以便能够包含C++动态库。因此,对我来说,大小是一个真正的因素。 - Erik
显示剩余7条评论

6
只要限制使用的功能,C++相比C不会有太大的性能损失。你需要避免使用以下功能:异常、RTTI,并尽可能保持类层次结构的扁平化(并谨慎使用虚函数)。

1
无论实现方式如何,例如异常、继承和RTTI等设计都会占用更多的代码空间。当有疑问时,请进行性能分析。正确性和健壮性高于一切。 - Thomas Matthews
只要你只使用你需要的功能。 :-) - Bo Persson
谢谢您提供这个列表。我原本没有计划使用那些功能,但知道需要避免什么是很好的。 - Gregory Khrapunovich

3

只要您的嵌入式系统有足够的RAM和flash,C++就可以胜任。 C++运行时库(libstdc ++)很大,并且即使您仅使用C ++,也会与C标准库(libc)一起使用。


3
在嵌入式系统中,只需要包含必要的库文件。用于桌面版本的库通常要大得多,并且没有大小相关性。关于大小问题的论点是虚假的。请参阅我在SO上的其他帖子。 - Thomas Matthews
反驳 libstdc++ 需要 RAM 和 Flash 的事实是一个大胆的举动... - GT.
2
@Thomas:我希望你能找到一种方法使libstdc ++变小。 uClibc ++的存在是有原因的;可惜它相当不完整。 - R.. GitHub STOP HELPING ICE

2

你可以使用C++,但要非常小心。

对于大小,要密切关注链接器映射文件。你会发现它包含了很多不必要的东西,只是因为一个看似无害的声明。

对于速度,经常进行性能分析或随机暂停。很容易做出比你实际需要更多的newdelete,特别是在容器类中,还要特别小心像迭代器这样的东西。通常它们会给你提供一些你没有要求的帮助。

你可以通过汇编语言级别逐步执行代码,以确保它只执行你实际需要的操作,这与C代码应该大致相同。


1

编写高效的代码,无论是嵌入式还是其他领域,真正的关键在于程序员充分理解自己的决策所带来的影响。

在某种程度上,C++ 提供了更多机会,使得一些昂贵的功能看起来毫不费力。与 C++ 功能相当的功能通常需要更多的代码,这可能会导致对其潜在开销的更多思考。但这绝不是绝对的 - C(及其库)也有欺骗性的开销风险。

最终,没有什么能够替代理解每行代码所要求的内容。


1

我正在使用ARM9开发板进行硬件控制,并在500 MHz的板子上同时使用C和C++应用程序。您可以选择语言以及如何实现逻辑来实现功能。因为我在控制硬件的一整天中没有发现任何问题运行我的应用程序。

编写程序时,请仔细选择变量,检查它是否有多余的指令/循环,初始化。还要在编译时使用Gcc优化标志。

我在500 MHz ARM 9板上运行我的Qt应用程序和C程序时没有遇到任何问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接