20

我曾在许多不同的嵌入式系统上工作过。他们都使用typedefs (或#defines)来表示诸如UINT32.

这是一种很好的技术,因为它可以让程序员了解类型的大小,并使您更加意识到溢出等的机会。

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

那么,什么会影响您创建和执行项目特定类型的决定呢?

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

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

对于其他一切,类型的重要性较低。
您需要小心不要导致溢出,并且可能需要注意寄存器和堆栈的使用。这可能会导致你UINT16UCHAR。但是,使用诸如UCHAR可以添加编译器“绒毛”之类的类型。因为寄存器通常较大,一些编译器可能会添加代码以强制将结果转换为类型。

我++;
可以变成
添加 REG,1
与 REG,0xFF
这是不必要的。

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

考虑到嵌入式软件的限制,对于一个需要很多人参与的项目来说,最好的策略是什么——并不是所有的人都具有相同的经验水平。

4

9 回答 9

17

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

  1. 在您希望它们适合寄存器的意义上,局部变量与结构成员和数组不同。在 32b/64b 目标上,int16_t与本地 int 相比,本地可以使代码变慢,因为编译器必须根据int16_t. 虽然 C99 定义了一个intfast_ttypedef,但 AFAIK 一个普通的 int 也可以放入寄存器中,而且它肯定是一个较短的名称。

  2. 喜欢这些 typedef 的组织几乎总是以其中的几个结尾(INT32, int32_t, INT32_T无限)。因此,在某种程度上,使用内置类型的组织最好只有一组名称。我希望人们使用来自 stdint.h 或 windows.h 或任何现有的类型定义;当目标没有那个 .h 文件时,添加一个文件有多难?

  3. typedef 理论上可以帮助可移植性,但我从来没有从它们那里得到任何东西。是否有一个有用的系统可以从 32b 目标移植到 16b 目标?是否有一个 16b 系统可以轻松移植到 32b 目标?此外,如果大多数 var 是整数,您实际上会从新目标上的 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 转换。由一个有秩序的组织编写的每个程序都需要 typedefs)。

  5. 当然,在 C++ 中,我们可以通过在模板类实例中使用重载运算符和其他内容包装数字来使类型系统更加严格。这意味着您现在将收到格式为“class Number<int,Least,32> has no operator+ overload for argument of type class Number<unsigned long long,Fast,64>, Candidate are...”形式的错误消息。也不要称其为“可读性”。您正确实现这些包装类的机会是微乎其微的,而且大多数时候您将等待无数模板实例化编译。

于 2008-08-17T19:15:31.027 回答
7

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

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

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

于 2008-08-10T09:03:24.057 回答
4

我的观点是,如果您依赖于最小/最大/特定大小,不要仅仅假设(比如说)anunsigned int是 32 个字节 -uint32_t而是使用(假设您的编译器支持 C99)。

于 2008-08-11T04:29:28.650 回答
4

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

于 2008-08-20T14:12:16.360 回答
3

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

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

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

于 2008-08-24T14:48:54.007 回答
1

一致性、便利性和可读性。“UINT32”比“unsigned long long”更具可读性和可写性,这对于某些系统来说是等价的。

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

于 2008-08-10T08:11:40.730 回答
1

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

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

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

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


这也适用于该领域的 C++;例如。JSF C++ 编码标准

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

于 2015-06-10T16:43:56.180 回答
1

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

当你对所有东西都进行了测试时,它可能会咬你一口,但它仍然会在你的目标系统上中断,因为 anint突然只有 16 位长。

于 2016-06-20T10:07:49.380 回答
0

也许我很奇怪,但我使用 ub、ui、ul、sb、si 和 sl 作为我的整数类型。也许 16 位的“i”看起来有点过时,但我更喜欢 ui/si 的外观而不是 uw/sw。

于 2010-08-14T03:23:24.137 回答