关于C#:何时在嵌入式系统中使用类型抽象

关于C#:何时在嵌入式系统中使用类型抽象

When should I use type abstraction in embedded systems

我已经研究了许多不同的嵌入式系统。它们都已将typedef(或#defines)用于UINT32之类的类型。

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

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

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

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

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

对于其他所有类型,重要性都较小。
您需要注意不要引起溢出,并且可能需要当心寄存器和堆栈的使用。这可能会导致您进入UINT16UCHAR
但是,使用诸如UCHAR之类的类型可以添加编译器'fluff'。由于寄存器通常较大,因此某些编译器可能会添加代码以将结果强制为类型。
[cc lang="c"] i ++;
可以变成
[cc lang="c"] ADD REG,1
AND REG,0xFF
这是不必要的。

所以我认为我的问题应该是:

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


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

  • 局部变量在您希望它们适合寄存器的意义上不同于结构成员和数组。在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是整数,则实际上您将从新目标的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 对于类型类Number 的参数没有运算符+重载,候选对象是..."我也不要将其称为"可读性"。正确实现这些包装器类的机会微乎其微,并且在大多数情况下,您将等待无数模板实例的编译。


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

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

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


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


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


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

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

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


    使用使您的代码在PC上进行单元测试时更具可移植性。

    当您对所有内容进行测试时,它可能会非常咬人,但由于int突然只有16位长,因此它仍然会在目标系统上中断。


    如果您的嵌入式系统在某种程度上是安全性至关重要的系统(或类似系统),则强烈建议(如果不需要)在普通类型上使用typedef。

    作为传统知识。之前已经说过,MISRA-C有一个(建议)规则可以这样做:

    Rule 6.3 (advisory): typedefs that indicate size and signedness should be used in place of the basic numerical types.

    (摘自MISRA-C 2004;这是MISRA-C 1998的规则13(adv))

    这在这方面也适用于C ++。例如。 JSF C ++编码标准:

    AV Rule 209 A UniversalTypes file will be created to define all sta
    ndard types for developers to use. The types include: [uint16, int16, uint32_t etc.]


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

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


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


    推荐阅读