嵌入式 Linux 中的 C 与 C++
Posted
技术标签:
【中文标题】嵌入式 Linux 中的 C 与 C++【英文标题】:C vs C++ in embedded Linux 【发布时间】:2011-02-28 19:37:59 【问题描述】:我正在为嵌入式 Linux (ARM) 开发应用程序。它将执行 500 次/秒,因此速度很重要。我更喜欢使用 C++,但我担心它会比 C 慢,即使我避免使用像虚函数这样的花哨功能。 是否有使用 C 的理由,或者用 C++ 编写也一样好?
【问题讨论】:
虚拟功能不是您应该避免的事情之一。在 C 中手动实现相同的功能不会比编译器生成的版本快,并且 C++ 编译器擅长在不需要时将其优化掉。 @Martin 说得对。虚函数只需要调用一两条指令,如果这确实是你需要的,那么无论如何你都必须在 C 中这样做。 这里有一些有趣的相关阅读:***.com/questions/2039444/… 尝试 C 和 C++,然后在嵌入式设备上测量生成的二进制文件。如果您不想编写应用程序的 2 个版本,只需使用您更熟悉的语言 (C++)。 【参考方案1】:一般来说,C++ 不会比 C 造成运行时间损失(RTTI 等少数情况除外)。
除了在一些奇怪的情况下,编译器应该能够确定在编译时调用哪个虚函数,因此不会增加开销。
编辑:好的,有这么多种编译器、CPU、运行时库、操作系统,C++ 的某些功能可能会创建较慢的代码,有些功能可能会创建更快的代码。
但是我们是否都同意 C++ 不再自动从嵌入式使用中排除?
【讨论】:
如果您将 C++ 用作“丑陋的 C”(到处都是额外的强制转换),那么您所说的是真的。但是一旦你开始使用任何让 C++“舒适”的东西,就会有很多无法优化的开销。 虚拟函数和模板应该是零成本的。如果没有在任何体面的编译器上调用,例外是零成本。 Dynamic_cast 通常可以在没有 RTTI 的情况下工作 - 带有一些警告。代码大小可能是一个问题 - 但这当然取决于您所说的嵌入是什么意思 当您可以从正常代码流中删除 C 错误处理时,异常会产生负成本。优化器可以轻松针对非异常情况进行优化,但不知道 C 函数在出错时返回 0,1 还是 -1。 我在具有 32K 闪存的嵌入式 ARM 上使用 C++11 和元编程!我发现如果您使用现代编译器(我使用 GCC 4.6),您可以将 C 代码移植到 C++,有时会看到大小减小。 constexpr 是一个重要的工具。 如果您的代码中有任何重要的数据结构,STL 容器(使用模板实现)实际上是一个巨大的胜利。这些实现尽可能好 - 并且可能比您自己编写的更好。如有必要,您还可以池分配。【参考方案2】:在 C++ 中,您可以使用模板元编程之类的东西在编译时解决 C 或任何其他过程编程语言必须在运行时执行的几种情况。
我应该说更多。模板元编程和一些类继承技巧真的很棒。它可以为您节省大量处理时间,否则您会通过“ifing”和“switching”来花费。
这意味着如果执行得当,C++ 实际上可以比 C 更快。
显然,您可以使用 C++“在 C 中”进行编程,而且您根本不会受到任何惩罚。 如果你不太喜欢 C++,我建议你做一个“C on C++”或“C with C++ extensions”只是为了利用 C++ 的改进,但你将拥有的真正优势是通过编程 C++方式。在那里你会看到 C++ 是,大部分时候,或者比 C 更快或更干净,或者至少和 C 一样快。
不要害怕。面对 C++。 在 stdc++(针对 libc)之后,代码大小几乎没有开销。如果您的应用程序的大小从中等到大,它将被稀释。
我使用从简单的 8 位 ATmega 到 Marvell 的 ARM9 的 C++,经过 AVR32 UC3 和 Cortex-M3,并且总是发现它有利可图。
如果您在特定情况下需要具体建议,请随时提出。
【讨论】:
这就是我所希望的答案。即使是简单的事情,比如能够在需要时声明一个 var(而不是在 C 中块的开头),C++ 编写起来也更有趣。当然,我更喜欢类封装而不是 C 全局函数。我希望避免动态内存分配或任何奇异的东西。 C++ 在嵌入式领域的优势是深远的。我发现 __attribute((always_inline)) 在将抽象接口编译为虚无时是天赐之物,只会对重要的东西进行更强大的静态检查。【参考方案3】:选择 C 而不是 C++ 的主要原因是编译后的二进制文件的大小,这可能是嵌入式系统的真正限制。
在性能方面,如果您使用正确的语言,则没有可衡量的差异。您可以像编写慢速 C++ 一样轻松编写慢速 C 代码,只要您了解您正在编写的内容的底层机制,您应该可以使用其中任何一种。
【讨论】:
只要 C 版本与 C++ 版本功能等效,大小差异可以忽略不计。例如,如果 C++ 版本使用继承,则 C 版本也必须编写等效的代码,没有捷径。根据我的经验,大小不是问题。 @Thomas:这根本不是真的,因为许多被拉入的库代码将具有所需功能的巨大超集,并且链接器无法确定哪些部分可以是被排除在外。静态链接 C hello world 程序与任何健全的 C 库(任何 BSD、uClibc、Bionic 等 - 只是不是 glibc,其 stdio 基本上是用 C++ 编写的)并使用 iostream 比较等效的静态链接 C++ 程序。 @R.. :如果您选择合适的 libc 实现,您应该公平并选择同样合适的 iostream。 Dietmar Kuehl 已经证明(大约 10 年前)Hello, World 在 C++ 中可以更小。原因很简单:链接器可以从printf()
实现中消除operator<<ostream&, float)
,但不能消除case 'f':
。
库大小(理论上)在某些环境下可能是个问题,但原问题提到嵌入式Linux,所以它至少是32位系统,操作系统本身也足够大。所以我怀疑在这种情况下大小很重要:)
我目前正在构建一个基于 linux 的嵌入式系统,我选择在设备中添加更多闪存,以便能够包含 c++ 动态库。所以至少对我来说,尺寸是一个真正的因素。【参考方案4】:
只要您限制使用的功能,C++ 的性能不会超过 C。您要避免的功能包括:异常、RTTI,并保持类层次结构为尽可能平坦(并谨慎使用虚函数)。
【讨论】:
无论实现方式如何,异常、继承和RTTI等设计都会占用更多的代码空间。当有疑问的个人资料。正确性和稳健性高于一切。 只要你只使用你需要的功能。 :-) 感谢您提供此列表。我不打算使用这些功能,但很高兴知道要避免什么。【参考方案5】:只要您的嵌入式系统中有足够的 RAM 和闪存,C++ 就可以了。 C++ 运行时库 (libstdc++) 很大,并且是 C 标准库 (libc) 的补充,即使您只使用 C++。
【讨论】:
在嵌入式系统中,只包含需要的库。用于桌面版本的库通常要大得多,并且没有大小相关性。关于尺寸问题的虚假信息。请参阅我在 SO 上的其他帖子。 反对 libstdc++ 需要内存和闪存这一事实是一个大胆的举动...... @Thomas:我希望看到你找到一种方法让libstdc++
变小。 uClibc++
存在是有原因的;遗憾的是,它相当不完整。【参考方案6】:
您可以使用 C++,但要格外小心。
对于大小,请密切注意您的链接器映射文件。你可以从一个看似无辜的声明中找到它,其中包括大量你不需要的东西。
经常用于速度、配置文件或random-pause。做比你真正需要的更多的new
s 和delete
s 是超级容易的,特别是对于容器类,并且要非常小心像迭代器这样的东西。
他们经常在你不请自来的帮助下帮您。
您可以在汇编语言级别单步执行代码,以确保它只执行您实际需要的操作,这应该与 C 代码大致相同。
【讨论】:
【参考方案7】:确定嵌入式或其他方式的高效代码大小和速度的真正关键在于程序员充分了解其决策的含义。
在某种程度上,C++ 提供了更多机会,让昂贵的东西看起来看似无辜。与 C++ 功能等效的功能通常需要在页面上使用更多墨水,这可能会导致对其潜在费用的更多考虑。但这绝不是绝对的——C(及其库)也有看似无辜的费用的风险。
最终,没有什么可以替代理解您在每一行代码中所要求的内容。
【讨论】:
【参考方案8】:我正在使用 ARM9 板进行硬件控制,并且在 500 Mhz 板上同时使用 C 和 C++ 应用程序。使用语言以及如何实现逻辑来实现功能取决于您。因为我在控制硬件的那天运行我的应用程序没有发现任何问题。
在编写程序时,请仔细选择变量,检查是否有额外的指令/循环、初始化。编译时也使用 Gcc 优化标志。
在 500 Mhz ARM 9 板上运行我的 Qt 应用程序和 C 程序没有任何问题。
【讨论】:
以上是关于嵌入式 Linux 中的 C 与 C++的主要内容,如果未能解决你的问题,请参考以下文章
在 C++ 中扩展嵌入式 Python - 设计与 C++ 实例交互