C++在嵌入式系统中需要避免哪些特性?
请按照以下原因分类回答:
- 内存使用
- 代码大小
- 速度
- 可移植性
编辑:以ARM7TDMI和64k RAM作为目标,来限制回答的范围。
RTTI 和异常处理:
模板:
虚函数和继承:
避免使用某些功能应该始终基于对您的软件在您的硬件上,在您选择的工具链下,受您的领域约束时行为的定量分析。在C++开发中有很多传统智慧“不适用”是基于迷信和古代历史而不是硬数据。不幸的是,这经常导致编写大量额外的解决方法代码,以避免使用某些功能,因为某个时候,某个地方的某个人曾经遇到过问题。
阅读Rationale关于早期嵌入式C++标准的理由是一件有趣的事情。
这篇文章也涉及EC++。
嵌入式C++标准是C++的一个合适子集,即它没有增加任何内容。以下语言特性被删除:
在维基页面上指出,Bjarne Stroustrup表示(关于EC++ std):“据我所知,EC++已经死亡(2004年),如果它还没有死亡,那么它应该死亡。” Stroustrup建议参考Prakash答案引用的文档。
如果使用ARM7且没有外部MMU,动态内存分配问题可能更难调试。我建议在指南列表中加入“谨慎使用new/delete/free/malloc”。
ABORT
异常的额外电路,但如果您没有这些实现,则由于非对齐访问而导致的错误很难排查。const char x[] = "ARM7TDMI";
unsigned int y = *reinterpret_cast<const unsigned int*>(&x[3]);
printf("%c%c%c%c\n", y, y>>8, y>>16, y>>24);
在大多数系统中,除非您使用自己的实现从自己的托管堆中提取内存,否则不建议使用 new / delete。是的,这可能需要一些工作,但您正在处理内存受限的系统。
我不会说在这方面有一个硬性规定;这取决于您的应用程序。嵌入式系统通常具有以下特点:
就像任何其他开发一样,您应该权衡您提到的所有观点并根据给定/派生的要求进行平衡。
时间函数通常是依赖于操作系统的(除非你重写它们)。使用自己的函数(特别是如果你有一个RTC)
只要你有足够的代码空间,模板就可以使用 - 否则不要使用它们
异常也不是很可移植
printf函数不写入缓冲区是不可移植的(你需要与文件系统连接才能用printf写入FILE*)。仅使用sprintf、snprintf和str*函数(strcat、strlen),当然还有它们的宽字符对应项(wcslen...)。
如果速度是问题,也许你应该使用自己的容器而不是STL(例如std::map容器,以确保一个键等于2个比较,'less'运算符(a [less than] b == false && b [less than] a == false意味着a == b)。'less'是std::map类(不仅仅是它)接收的唯一比较参数。这可能会导致一些关键例程的性能损失。
模板、异常会增加代码大小(你可以确定这一点)。有时候,即使是性能也会受到更大代码的影响。
内存分配函数可能需要重新编写,因为它们在许多方面(特别是在处理线程安全内存分配时)依赖于操作系统。
malloc使用_end变量(通常在链接器脚本中声明)来分配内存,但在“未知”环境中这不是线程安全的。
有时候你应该使用Thumb而不是Arm模式。这可以提高性能。
所以对于64k内存,我会说C++带有一些很好的功能(STL、异常等),可能过于复杂了。我肯定会选择C。
我曾经使用过GCC ARM编译器和ARM自己的SDT,以下是我的评论:
ARM SDT生成的代码更紧凑、更快,但价格非常昂贵(每个许可证超过5千欧元!)。在我之前的工作中,我们使用了这个编译器,效果还不错。
GCC ARM工具也非常好用,我在自己的项目(GBA/DS)中使用它。
使用“thumb”模式可以显著减小代码大小。在ARM的16位总线变体(如GBA)上,也有速度优势。
64k对于C++开发来说太小了。在这种环境下,我会使用C和汇编语言。
在这样一个小平台上,您必须注意堆栈使用情况。避免递归、大型自动(本地)数据结构等。堆使用也将是一个问题(new、malloc等)。C将为您提供更多控制这些问题的能力。