我应该何时在嵌入式系统中使用类型抽象

问题描述 投票:18回答:9

我曾经在许多不同的嵌入式系统上工作过。他们都使用typedefs(或#defines)等类型的UINT32

这是一个很好的技术,因为它将类型的大小驱动到程序员家,让你更加意识到溢出的可能性等。

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

那么应该影响您创建和实施项目特定类型的决策呢?

编辑我想我设法失去了我的问题的要点,也许它真的是两个。

使用嵌入式编程,您可能需要特定大小的接口类型,以及处理受限资源(如RAM)。这是无法避免的,但您可以选择使用编译器中的基本类型。

对于其他一切,类型的重要性较低。 您需要注意不要引起溢出,并且可能需要注意寄存器和堆栈的使用。这可能会导致你到UINT16UCHAR。使用UCHAR等类型可以添加编译器'fluff'。由于寄存器通常较大,因此某些编译器可能会添加代码以强制将结果输入类型。

i++;
can become
ADD REG,1
AND REG, 0xFF
which is unecessary.

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

鉴于嵌入式软件的限制,为一个项目设置的最佳策略是什么,这个项目将有很多人参与其中 - 并非所有人都具有相同的经验水平。

c embedded
9个回答
16
投票

我很少使用类型抽象。以下是我的论点,按主观性的递增顺序排序:

  1. 局部变量与结构成员和数组的不同之处在于您希望它们适合寄存器。在32b / 64b目标上,本地int16_t可以使代码比本地int更慢,因为编译器必须根据int16_t的语义向/ force / overflow添加操作。虽然C99定义了一个intfast_t typedef,但是AFAIK一个普通的int也适合寄存器,它肯定是一个较短的名字。
  2. 喜欢这些typedef的组织几乎总是最终得到其中几个(INT32, int32_t, INT32_T,ad infinitum)。因此,使用内置类型的组织在某种程度上只有一组名称会更好。我希望人们使用stdint.h或windows.h中的typedef或任何现有的;当一个目标没有那个.h文件时,加一个目标有多难?
  3. 从理论上讲,typedef可以帮助实现可移植性,但是,我从来没有从中获得过一些东西。有没有一个有用的系统可以从32b目标移植到16b目标?是否有一个16b系统,移植到32b目标是不是很容易?此外,如果大多数变量都是整数,你实际上会从新目标的32位获得一些东西,但如果它们是int16_t,你就不会。而难以移植的地方往往需要人工检查;在尝试端口之前,您不知道它们在哪里。现在,如果有人认为如果你在所有地方都有typedef就可以轻松移植东西 - 当端口出现时,很少有系统发生,编写一个脚本来转换代码库中的所有名称。这应该按照“无需人工检查”逻辑工作,并将工作推迟到实际带来好处的时间点。
  4. 现在,如果可移植性可能是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转换。每个由有序组织强制命令类型的程序编写的程序都充斥着它)。
  5. 当然在C ++中,我们可以通过在模板类实例化中使用重载运算符和东西包装数字来使类型系统更加严格。这意味着您现在将获得“class Number <int,Least,32>”的错误消息,对于类型类型<unsigned long long,Fast,64>的参数,没有运算符+重载,候选者是...“I也不要称之为“可读性”。您正确实现这些包装类的机会是微观的,并且大多数时候您将等待无数的模板实例化进行编译。

7
投票

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

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

即使您不能使用C ++或其他允许您以这种方式编写代码的语言,您至少可以使用C类型系统来帮助您通过眼睛捕获错误。 (这实际上是Simonyi的匈牙利符号的原始意图。)仅仅因为编译器不会因为向meter_t添加gram_t而大喊大叫并不意味着你不应该使用类似的类型。然后,代码审查将更有效地发现单位错误。


4
投票

我的观点是,如果你依赖于最小/最大/特定大小,不要只假设(比方说)unsigned int是32字节 - 改为使用uint32_t(假设你的编译器支持C99)。


4
投票

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


3
投票

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

从个人的角度来看,使用typedef不会混淆某些类型的大小(以位/字节为单位)。我已经看到主要开发者尝试使用标准类型进行两种开发方式,例如int和使用自定义类型,例如UINT32。

如果代码不可移植,那么使用typedef几乎没有什么好处,但是,如果像我一样,那么你可以使用两种类型的软件(便携式和固定环境),那么保持标准并使用cutomised类型会很有用。至少就像你说的那样,程序员非常清楚他们正在使用多少内存。另一个需要考虑的因素是,您确定代码不会被移植到另一个环境是多么“确定”?我已经看到处理器特定的代码必须被翻译为硬件工程师突然不得不改变一个板,这不是一个很好的情况,但由于自定义typedef它可能会更糟糕!


1
投票

一致性,便利性和可读性。 “UINT32”比“unsigned long long”更具可读性和可写性,这与某些系统相当。

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


1
投票

如果您的嵌入式系统在某种程度上是一个安全关键系统(或类似系统),强烈建议(如果不是必需的话)使用typedef而不是普通类型。

作为TK。之前已经说过,MISRA-C有一条(咨询)规则:

规则6.3(建议):应使用指示大小和签名的typedef来代替基本数字类型。

(来自MISRA-C 2004;它是MISRA-C 1998的规则#13(adv))


同样适用于该领域的C ++;例如。 JSF C++ coding standards

AV规则209将创建UniversalTypes文件以定义供开发人员使用的所有标准类型。类型包括:[uint16,int16,uint32_t等]


1
投票

使用<stdint.h>使您的代码更便于在PC上进行单元测试。

当你对所有东西进行测试时,它会很难咬你,但它仍会在目标系统上破坏,因为int突然只有16位长。


0
投票

也许我很奇怪,但我使用ub,ui,ul,sb,si和sl作为我的整数类型。或许16位的“i”似乎有点陈旧,但我喜欢ui / si的外观比uw / sw更好。

© www.soinside.com 2019 - 2024. All rights reserved.