我曾参与过许多不同的嵌入式系统。他们都用过 typedefs(或 #defines) 对于诸如 UINT32.

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

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

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

编辑我认为我设法失去了问题的要点,也许确实是两个。

通过嵌入式编程,您可能需要特定大小的接口类型,并应对 RAM 等有限资源。这是无法避免的,但您可以选择使用编译器中的基本类型。

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

i++;
可以变成
ADD REG,1
AND REG, 0xFF
这是不必要的。

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

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

有帮助吗?

解决方案

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

  1. 局部变量与结构成员和数组不同,因为您希望它们适合寄存器。在 32b/64b 目标上,本地 int16_t 与本地 int 相比,可以使代码变慢,因为编译器必须根据以下语义向 /force/ Overflow 添加操作 int16_t. 。虽然 C99 定义了 intfast_t typedef,据我所知,一个普通的 int 也可以放入寄存器中,而且它的名称肯定更短。

  2. 喜欢这些 typedef 的组织几乎总是会得到其中的几个(INT32, int32_t, INT32_T, ,无穷无尽)。因此,在某种程度上,使用内置类型的组织只拥有一组名称会更好。我希望人们使用 stdint.h 或 windows.h 或任何现有的类型定义;当目标没有该 .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 转换。由有序组织编写的每个强制使用 typedef 的程序都充斥着这种情况)。

  5. 当然,在 C++ 中,我们可以通过使用重载运算符和内容将数字包装在模板类实例化中,从而使类型系统更加严格。这意味着您现在将收到以下形式的错误消息:“class Number<int,Least,32> 对于类型为 class Number<unsigned long long,Fast,64> 的参数没有运算符+重载,候选者是...”也不要称之为“可读性”。正确实现这些包装器类的机会很小,并且大多数时候您将等待无数模板实例化进行编译。

其他提示

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

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

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

我的意见是,如果您取决于最小/最大/特定尺寸 只是假设(比如说)一个 unsigned int 是 32 字节 - 使用 uint32_t 相反(假设您的编译器支持 C99)。

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

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

从个人角度来看,使用 typedef 不会对某些类型的大小(以位/字节为单位)产生混淆。我见过主要开发人员尝试使用标准类型(例如int 并使用自定义类型,例如UINT32。

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

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

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

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

作为 传统知识。 之前已经说过, 米斯拉-C 有一条(咨询)规则可以这样做:

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

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


这同样适用于 C++ 领域;例如。 JSF C++ 编码标准:

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

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

当您对所有内容进行测试时,它可能会非常严重,但它仍然会在您的目标系统上崩溃,因为 int 突然只有 16 位长。

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top