最佳实践和行业标准随着时间的推移而发展和演变,但它们代表了指导智慧的快照。尽管技术进步使先前已知的最佳实践无效,但最佳实践可能发展缓慢,并且经常变得根深蒂固。在嵌入式开发领域使用 C 语言特征也遭遇了同样的命运。许多最佳实践起源于 80 年代和 90 年代,当时编译器还很古怪,而微控制器确实资源有限。从那时起,编译器和微控制器已经取得了长足的进步,许多“被禁止”的特征和设计技术可能不再如此。
特征1 – Float
在嵌入式系统中使用Float长期以来一直受到强烈反对。Float 传统上存在许多潜在的问题来源。首先,微控制器没有浮点单元。在大多数情况下,对于最低端的微控制器,这种说法在今天仍然适用,但包含浮点单元 (FPU) 的成本已大幅下降到中档和通用微控制器开始包含 FPU 的程度。微控制器技术的进步现在还包括数学函数的硬件加速,即使没有FPU也可以帮助加快计算速度。
其次,在没有 FPU 的情况下使用float需要编译器引入庞大而缓慢的软件库。自20世纪后期以来,编译器技术得到了极大的改进,即使在软件中的8位微控制器上执行浮点计算也被优化到可以忽略不计的程度。技术正在发生变化,嵌入式开发人员需要随之改变。
特征2–malloc
Malloc允许开发人员在程序执行期间动态分配内存,如果在嵌入式系统中使用不当,它可能是一个非常危险的工具。传统上,在资源受限的系统中完全禁止使用malloc,这是有原因的。当堆变得支离破碎时会发生什么?开发人员如何处理内存分配失败?内存泄漏怎么办?这些都是困难的程序,需要代码空间和马力来处理,传统的微控制器无法处理。
微控制器不再“传统”。2015年,低成本微控制器的时钟速度可能超过200 MHz,闪存空间超过1 MB,RAM高达64 KB(高达256 MB)。即使在一个不那么强大的系统中,也有正确实现和使用malloc的工具。应用程序可能也很好地保证了这一点,因此不应该仅仅因为过时的最佳实践就从一开始就将其排除在考虑之外。
特征3 – printf
一般来说,嵌入式开发人员在嵌入式系统中使用 C 库函数被认为是不好的做法。大多数 C 库不是可重入的,通常体积庞大且执行缓慢(或者是吗?),或者以阻塞执行直到完成的方式实现。printf的使用属于这些类别中的许多类别,并且已避免在嵌入式系统中使用。
如前所述,在编译器优化和硬件改进之间发生了很多变化,使用printf是过去的罪恶感。考虑到典型的32 kB闪存空间,该函数虽然被认为是“大”,但占用的代码空间很小。典型的实现将printf用作阻塞函数,这会影响实时响应并占用潜在的共享资源。通过将printf链接到循环缓冲区和中断驱动程序传输驱动程序,可以轻松克服响应问题,从而允许程序执行继续,同时驱动程序执行必要的工作。
特征4 – memset
大多数涉及内存操作或动态内存的 C 特征最终都在禁止中。原因很明显,许多嵌入式开发人员和团队在过去使用这样的特征时都吃了亏。当诸如 malloc 或 memset 之类的功能最有意义时,尽管不应回避它们。相反,开发人员应该确保他们完全了解如何使用该功能,正确使用需要哪些预防措施,在最坏的情况发生时如何恢复,当然还有适当的测试以确保一切按计划进行。
特征5 – C位字段
一个概念是使用在C标准中定义模糊的特征,这个概念的使用并不植根于编译器技术的进步或硬件的进步。这种特征的一个很好的例子是位字段。使用位字段的最大问题是通常存在可移植性问题。一个简单的例子是,位的排序不是标准的,编译器通常可以重新排列这些位,使之成为最有效的实现方式。向整体结构中添加填充字节也有问题。
一般的经验法则是避免使用位字段,但是有很多情况下它们是有意义的,甚至可移植性问题也无关紧要。使用位字段的一个很好的例子是创建一个结构,该结构用于创建初始化驱动程序或应用程序的配置表。通过写入初始化来读取位的值,重新排序甚至填充字节都不是问题。
结论
标准和最佳实践旨在帮助开发人员避免搬起石头砸自己的脚,但它们仅仅是最佳实践。一个标准可能会说禁止使用C特征,但事实是,这取决于开发人员在他自己独特的情况下决定传统智慧是否适用。挑战我们的先入之见,并确保我们理解为什么那些最佳实践是适当的,这与遵循它们同样重要。不要在不了解它们的应用、风险和回报的情况下就放弃它们。嵌入式开发人员在考虑使用这些“禁止”的特征时,需要了解它们的时间、性能和大小限制。