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

dnulla 发布于 2018-03-14 c 最后更新 2018-03-14 01:06 542 浏览

我曾在许多不同的嵌入式系统上工作。他们都使用typedefs(或#defines)类型,如UINT32。 这是一个很好的技术,因为它将程序员的类型驱动回家,让你更加意识到溢出的可能性等等。 但是在某些系统上,您知道编译器和处理器在项目的整个生命周期内不会改变。 那么,什么会影响你决定创建和执行项目特定类型? 编辑 我想我设法失去了我的问题的要点,也许它确实是两个。 使用嵌入式编程,您可能需要特定尺寸的接口类型,并且还要处理受限的资源,例如RAM。这是无法避免的,但您可以选择使用编译器中的基本类型。 对于其他所有类型的重要性不那么重要 你需要小心,不要导致溢出,可能需要注意注册和堆栈使用情况。这可能会导致你到UINT16,UCHAR。 然而,使用UCHAR等类型可以增加编译器“绒毛”。由于寄存器通常较大,因此一些编译器可能会添加代码以强制结果进入类型

i++;
可以变成
ADD REG,1
AND REG, 0xFF
这是不必要的。 所以我认为我的问题应该是: - 考虑到嵌入式软件的限制,对于一个项目来说最好的策略是什么,这个项目会有很多人从事这项工作 - 并不是所有人都具有相同的经验水平。
已邀请:

mest

赞同来自:

一致性,便利性和可读性。 “UINT32”比“unsigned long long”更具可读性和可写性,这对于某些系统是等效的。 此外,编译器和处理器可能在项目生命周期中得到修复,但该项目的代码可能会在另一个项目中找到新的生命。在这种情况下,拥有一致的数据类型非常方便。

pquo

赞同来自:

C99标准有许多标准大小的整数类型。如果你可以使用支持C99的编译器(gcc),你可以在<stdint.h>中找到它们,你可以在你的项目中使用它们。 另外,在嵌入式项目中,将类型用作单位转换之类的“安全网”可能尤其重要。如果您可以使用C++,那么我知道有一些“单元”库可让您使用由C++类型系统定义的物理单元(通过模板),这些单元被编译为底层标量类型的操作。例如,这些库不会让您将distance_t添加到mass_t,因为这些单元不对齐;你实际上会得到一个编译器错误。 即使你不能用C++或者其他语言来编写代码,你至少可以使用C类型系统来帮助你捕捉像这样的错误。 (这实际上是Simonyi的匈牙利符号的原意)。仅仅因为编译器不会因为将meter_t添加到gram_t而嚷嚷你并不意味着你不应该使用那样的类型。代码评审在发现单元错误时会更有效率。

weos

赞同来自:

我的意见是,如果你依赖于最小/最大/特定的大小不要,那么假设unsigned int是32字节 - 取而代之的是使用uint32_t(假设你的编译器支持C99)。

rsaepe

赞同来自:

我很少使用类型抽象。这里是我的论点,按主观性的升序排序:

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

ksequi

赞同来自:

我喜欢使用stdint.h类型来定义系统API,因为他们明确地说出了多大的项目。回到过去的Palm OS系统,系统API使用一系列像“Word”和“SWord”这样的非常流行的类型来定义,这些类型是从非常经典的Mac OS继承而来的。他们进行了一次清理,而不是说Int16,这使得新手更容易理解API,尤其是那些系统上奇怪的16位指针问题。当他们设计Palm OS Cobalt时,他们再次更改了这些名称以匹配stdint.h的名称,使其更加清晰并减少了他们必须管理的typedefs数量。

vsit

赞同来自:

我相信MISRA标准建议(要求?)使用typedefs。 从个人角度来看,使用typedefs不会混淆某些类型的大小(以位/字节为单位)。我已经看到铅开发人员尝试通过使用标准类型开发两种方式,例如, int并使用自定义类型UINT32。 如果代码不可移植,那么在使用typedefs方面很少有真正的好处,但是,然而,如果像我那样你可以在这两种类型的软件上工作(便携式和固定环境),那么保持标准并使用cutomised类型可能很有用。至少就像你说的那样,程序员非常清楚他们使用了多少内存。另一个需要考虑的因素是,你是否确定代码不会移植到其他环境中?我看到处理器特定的代码必须翻译,因为硬件工程师突然不得不更换电路板,这不是一个好的情况,但由于自定义typedef,它可能会变得更糟!

lest

赞同来自:

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

punde

赞同来自:

如果您的嵌入式系统在某种程度上是 安全关键系统 (或类似的),那么强烈建议(如果不需要)使用普通类型的typedef 。 正如 TK。之前所说的那样, 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++ coding standards
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.]

hvelit

赞同来自:

使用<stdint.h>使您的代码更便于在电脑上进行单元测试。 当你对所有东西进行测试时,它会咬你很难,但是它仍然会在目标系统上崩溃,因为int突然只有16位长。