什么时候应该在嵌入式系统中使用类型抽象

时间:2020-03-05 18:38:49  来源:igfitidea点击:

我已经研究了许多不同的嵌入式系统。他们都对诸如UINT32之类的类型使用了typedef(或者#defines)。

这是一项很好的技术,因为它可以将类型的大小传递给程序员,并使我们更加意识到溢出等的机会。

但是在某些系统上,我们知道编译器和处理器在项目生命周期内不会改变。

那么,什么会影响我们创建和实施项目特定类型的决定?

编辑
我想我已经失去了问题的要旨,也许确实是两个。

对于嵌入式编程,我们可能需要特定大小的接口类型,并且还需要处理有限的资源(例如RAM)。这是不可避免的,但是我们可以选择使用编译器中的基本类型。

对于其他所有类型,重要性都较小。
我们需要注意不要引起溢出,并且可能需要当心寄存器和堆栈的使用。这可能会导致我们进入" UINT16"," UCHAR"。
但是,使用诸如" UCHAR"之类的类型可以添加编译器" fluff"。由于寄存器通常较大,因此某些编译器可能会添加代码以将结果强制为类型。

i++;

可以变成

ADD REG,1
AND REG, 0xFF

这是不必要的。

所以我想我的问题应该是:

考虑到嵌入式软件的局限性,为项目设置最佳的策略是什么,因为它将有很多人在从事该项目,但并非所有人都具有相同的经验水平。

解决方案

回答

一致性,方便性和可读性。 " UINT32"比" unsigned long long"更具可读性和可写性," UINT32"在某些系统中是等效的。

同样,在项目的整个生命周期中,编译器和处理器可能是固定的,但是该项目中的代码可能会在另一个项目中找到新的生命。在这种情况下,具有一致的数据类型非常方便。

回答

C99标准具有许多标准的大小整数类型。如果可以使用支持C99的编译器(gcc可以),则可以在<stdint.h>中找到它们,并且可以在项目中使用它们。

同样,在嵌入式项目中使用类型作为单位转换之类的"安全网"尤其重要。如果我们可以使用C ++,我知道那里有一些"单位"库,可让我们在由C ++类型系统(通过模板)定义的物理单位中工作,这些物理单位被编译为对基本标量类型的操作。例如,这些库不允许我们将distance_t添加到mass_t中,因为单位不对齐。我们实际上会得到一个编译器错误。

即使我们不能使用C ++或者其他可以通过这种方式编写代码的语言,也可以至少使用C型系统来一目了然地发现错误。 (这实际上是Simonyi匈牙利表示法的初衷。)仅仅因为编译器不会为我们在gram_t中添加meter_t而大喊大叫,并不意味着我们不应该使用这种类型。这样,代码审查将在发现单元错误时更加有效。

回答

我的意见是,如果我们取决于最小/最大/特定大小,则不要仅假设(说)" unsigned int"为32字节,而应使用" uint32_t"(假设编译器支持C99)。

回答

我很少使用类型抽象。这是我的论点,按主观性从高到低排序:

  • 局部变量与结构成员和数组的不同之处在于,我们希望它们适合寄存器。在32b / 64b目标上,本地int16_t'会使代码比本地int慢,因为编译器将不得不根据'int16_t'的语义向/ force /溢出添加操作。虽然C99定义了一个intfast_t` typedef,但是AFAIK一个普通的int也将适合一个寄存器,并且它肯定是一个较短的名字。
  • 喜欢这些typedef的组织几乎总是以其中的几种结尾(INT32,int32_t,INT32_T,无限)。因此,在某种程度上,使用内置类型的组织最好只拥有一组名称。我希望人们使用stdint.h或者windows.h或者任何现有的typedef。当目标没有该.h文件时,添加一个文件有多难?
  • 从理论上讲,typedef可以帮助实现可移植性,但是我从来没有从中获得过任何帮助。有没有可以将32b目标移植到16b目标的有用系统?有没有移植到32b目标的简单16b系统?而且,如果大多数var是int,则实际上我们将从新目标的32位中得到一些收益,但是如果它们是int16_t,则不会。无论如何,那些难以携带的地方往往都需要人工检查。在尝试端口之前,我们不知道它们在哪里。现在,如果有人认为到处都是typedef便很容易移植东西-当时间到了端口(这种情况很少发生在少数系统上)时,编写一个脚本来转换代码库中的所有名称。这应该根据"无需手动检查"逻辑来进行,并将工作推迟到实际上可以带来收益的时间点。
  • 现在,如果可移植性可能是typedef的理论优势,那么可读性肯定会付诸东流。只需看一下stdint.h:{int,uint} {max,fast,least} {8,16,32,64} _t。类型很多。一个程序有很多变量。真的很容易理解哪个需要为int_fast16_t和哪个为uint_least32_t吗?我们在它们之间无声地转换了多少次,使它们完全毫无意义? (我特别喜欢BOOL / Bool / eBool / boolean / bool / int转换。由有序组织强制typedef编写的每个程序都乱七八糟)。
  • 当然,在C ++中,通过使用重载的运算符和内容将数字包装在模板类实例中,可以使类型系统更加严格。这意味着我们现在将收到以下格式的错误消息:"类Number <int,Least,32>对于类型类Number <unsigned long long,Fast,64>的参数没有运算符+重载,候选对象是..."我也不要将其称为"可读性"。正确实现这些包装器类的机会很小,大多数情况下,我们将等待无数模板实例的编译。

回答

我喜欢使用stdint.h类型来定义系统API,这是因为它们明确指出了大项目。在Palm OS的早期,系统API是使用一堆从非常经典的Mac OS继承来的" Word"和" SWord"类型定义的。他们做了一个清理工作,而不是说Int16,它使API易于新手理解,特别是在该系统上出现16位指针异常问题的情况下。当他们设计Palm OS Cobalt时,他们再次更改了这些名称以匹配stdint.h的名称,从而使其更加清晰并减少了他们必须管理的typedef数量。

回答

我相信MISRA标准建议(要求?)使用typedef。

从个人角度来看,使用typedefs不会对某些类型的大小(以位/字节为单位)造成混淆。我已经看到领先的开发人员尝试通过使用标准类型(例如int并使用自定义类型,例如UINT32.

如果代码不是可移植的,则使用typedef几乎没有真正的好处,但是,如果像我一样,我们同时使用两种类型的软件(便携式和固定环境),则保持标准并使用定制的类型可能会很有用。至少像我们说的那样,程序员然后非常了解他们正在使用多少内存。要考虑的另一个因素是我们如何"确定"代码不会移植到另一个环境?我已经看到必须翻译处理器特定的代码,因为硬件工程师突然不得不更换电路板,这虽然不是一个很好的情况,但是由于自定义typedef,情况可能会更糟!

回答

也许我很奇怪,但是我将ub,ui,ul,sb,si和sl用于整数类型。也许16位的" i"似乎有些陈旧,但是我更喜欢ui / si的外观而不是uw / sw。