我曾参与过许多不同的嵌入式系统。对于typedef
等类型,他们都使用#defines
s(或UINT32
)。
这是一个很好的技术,因为它将类型的大小驱动到程序员家,让你更加意识到溢出的可能性等。
但在某些系统上,您知道编译器和处理器在项目的整个生命周期内都不会改变。
那么,什么应该影响您创建和实施项目特定类型的决定?
EDIT 我想我设法失去了我的问题的主旨,也许它真的是两个。
使用嵌入式编程,您可能需要特定大小的接口类型,以及处理受限资源(如RAM)。这是无法避免的,但您可以选择使用编译器中的基本类型。
对于其他所有类型,其重要性较低
您需要注意不要引起溢出,并且可能需要注意寄存器和堆栈的使用。这可能会引导您UINT16
,UCHAR
。
使用诸如UCHAR
之类的类型可以添加编译器'fluff'。由于寄存器通常较大,因此某些编译器可能会添加代码以强制将结果输入类型
i++;可以变成
ADD REG,1 AND REG, 0xFF这是不必要的。
所以我认为我的问题应该是: -
考虑到嵌入式软件的限制,为项目设置的最佳策略是什么,将有许多人参与其中 - 并非所有人都具有相同的经验水平。
答案 0 :(得分:16)
我很少使用类型抽象。以下是我的论点,按主观性的递增顺序排序:
在您希望它们适合寄存器的意义上,局部变量与结构成员和数组不同。在32b / 64b目标上,本地int16_t
可以使代码与本地int相比更慢,因为编译器必须根据int16_t
的语义向/ force / overflow添加操作。虽然C99定义了一个intfast_t
typedef,但是aFAIK一个普通的int也适合一个寄存器,它确实是一个较短的名称。
喜欢这些typedef的组织几乎总是最终得到其中几个(INT32, int32_t, INT32_T
,无限广告)。因此,使用内置类型的组织在某种程度上只有一组名称会更好。我希望人们使用stdint.h或windows.h中的typedef或任何现有的;当一个目标没有那个.h文件时,添加一个文件有多难?
typedef理论上可以帮助实现可移植性,但是,我从来没有从中获得过任何东西。有没有一个有用的系统可以从32b目标移植到16b目标?是否有一个16b系统,移植到32b目标是不是很容易?此外,如果大多数变量都是整数,那么你实际上会从新目标的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 ++中,我们可以通过在模板类实例化中使用重载的运算符和东西包装数字来使类型系统更加严格。这意味着您现在将获得“class Number< int,Least,32>”形式的错误消息,类型类的参数没有运算符+重载< unsigned long long,Fast,64>候选是...“I也不要称之为“可读性”。您正确实现这些包装类的机会是微观的,大多数时候您都会等待无数的模板实例化进行编译。
答案 1 :(得分:7)
C99标准有许多标准大小的整数类型。如果您可以使用支持C99(gcc)的编译器,您可以在<stdint.h>
中找到它们,您可以在项目中使用它们。
此外,在嵌入式项目中,将类型用作单位转换等类型的“安全网”尤其重要。如果你可以使用C ++,我知道有一些“单元”库可以让你在由C ++类型系统(通过模板)定义的物理单元中工作,这些物理单元被编译为底层标量类型的操作。例如,这些库不允许您向distance_t
添加mass_t
,因为这些单元没有对齐;你实际上会得到一个编译器错误。
即使您不能使用C ++或其他允许您以这种方式编写代码的语言,您至少可以使用C类型系统来帮助您捕获这样的错误。 (这实际上是Simonyi的匈牙利符号的原始意图。)仅仅因为编译器不会因为meter_t
添加gram_t
而对你大喊大叫并不意味着你不应该使用类似的类型。然后,代码审查将更有效地发现单位错误。
答案 2 :(得分:4)
我的意见是,如果你依赖于最小/最大/特定大小不只是假设(比方说)unsigned int
是32字节 - 请使用uint32_t
相反(假设您的编译器支持C99)。
答案 3 :(得分:4)
我喜欢使用stdint.h类型来定义系统API,因为它们明确说明了大的项目。回到Palm OS的旧时代,系统API是使用从非常经典的Mac OS继承而来的一堆诸如“Word”和“SWord”之类的诸如此类的非常类型来定义的。他们做了一个清理,改为说Int16,它使新手更容易理解API,特别是在该系统上出现奇怪的16位指针问题。当他们设计Palm OS Cobalt时,他们再次更改了这些名称以匹配stdint.h的名称,使其更加清晰,并减少了他们必须管理的typedef数量。
答案 4 :(得分:3)
我认为MISRA标准建议(要求?)使用typedef。
从个人角度来看,使用typedef不会混淆某些类型的大小(以位/字节为单位)。我已经看到主要开发者尝试使用标准类型进行两种开发方式,例如int和使用自定义类型,例如UINT32。
如果代码不可移植,那么在使用typedef时很少有真正的好处,然而,如果像我一样,那么你可以处理这两种类型的软件(便携式和固定式)然后保持标准并使用cutomised类型是有用的。至少就像你说的那样,程序员非常清楚他们正在使用多少内存。另一个需要考虑的因素是,您确定代码不会被移植到另一个环境是多么“确定”?我已经看到处理器特定的代码必须翻译为硬件工程师突然不得不改变一个板,这不是一个很好的情况,但由于自定义typedef它可能会更糟糕!
答案 5 :(得分:1)
一致性,便利性和可读性。 “UINT32”比“unsigned long long”更具可读性和可写性,这对某些系统来说是等效的。
此外,编译器和处理器可能在项目的生命周期内是固定的,但该项目的代码可能会在另一个项目中找到新的生命。在这种情况下,拥有一致的数据类型非常方便。
答案 6 :(得分:1)
如果您的嵌入式系统在某种程度上是 安全关键系统 (或类似),则强烈建议(如果不是必需的话) )使用typedef而不是普通类型。
正如 TK。之前所说, MISRA-C 有一条(建议)规则:
规则6.3(建议):表示大小和签名的typedef应该用来代替基本的数字类型。
(来自MISRA-C 2004;它是MISRA-C 1998的规则#13(adv))
同样适用于该领域的C ++;例如。 JSF C++ coding standards:
AV规则209 将创建UniversalTypes文件以定义所有sta 开发人员使用的ndard类型。类型包括:[uint16,int16,uint32_t等]
答案 7 :(得分:1)
使用<stdint.h>
使您的代码更便于在PC上进行单元测试。
当您对所有内容进行测试时,它会非常困难,但它仍会在您的目标系统上中断,因为int
突然只有16位长。
答案 8 :(得分:0)
也许我很奇怪,但我使用ub,ui,ul,sb,si和sl作为我的整数类型。或许16位的“i”似乎有点陈旧,但我喜欢ui / si的外观比uw / sw更好。