对于嵌入式代码,为什么我应该使用“uint_t”类型而不是“unsigned int”?

电器工程 C 嵌入式 海合会
2022-01-15 23:37:37

我正在使用 gcc 在 c 中为 STM32F105 编写应用程序。

在过去(对于更简单的项目),我总是将变量定义为charintunsigned int等。

我发现使用 stdint.h 中定义的类型很常见,例如int8_t, uint8_t,uint32_t等。这在我使用的多个 API 以及 ST 的 ARM CMSIS 库中都是如此。

我相信我理解我们为什么要这样做;让编译器更好地优化内存空间。我预计可能还有其他原因。

但是,由于 c 的整数提升规则,每当我尝试添加两个值、进行按位运算等时,我都会遇到转换警告。警告内容类似于conversion to 'uint16_t' from 'int' may alter its value [-Wconversion]. 这个问题在这里这里讨论。

int使用声明为or的变量时不会发生这种情况unsigned int

举几个例子,鉴于此:

uint16_t value16;
uint8_t value8;

我将不得不改变这一点:

value16 <<= 8;
value8 += 2;

对此:

value16 = (uint16_t)(value16 << 8);
value8 = (uint8_t)(value8 + 2);

这很丑陋,但如果有必要我可以这样做。以下是我的问题:

  1. 是否存在从无符号到有符号再回到无符号的转换会使结果不正确的情况?

  2. 是否有任何其他重要原因支持/反对使用 stdint.h 整数类型?

根据我收到的答案,看起来通常首选 stdint.h 类型,即使 c 转换uintint和返回。这就引出了一个更大的问题:

  1. value16 = (uint16_t)(value16 << 8);我可以通过使用类型转换(例如)来防止编译器警告。我只是在隐藏问题吗?有没有更好的方法来解决它?
4个回答

一个 17 到 32 位的符合标准的编译器int可以合法地使用以下代码做任何它想做的事情:

uint16_t x = 46341;
uint32_t y = x*x; // temp result is signed int, which can't hold 2147488281

想要这样做的实现可以合法地生成一个程序,该程序除了使用每个可以想象的协议在每个端口引脚上重复输出字符串“Fred”之外什么都不做。将程序移植到可以执行此操作的实现的可能性非常低,但理论上是可能的。如果要编写上面的代码,以保证不进行未定义行为,则需要将后面的表达式写为(uint32_t)x*xor 1u*x*x在 17 到 31 位之间的编译器上int,后一个表达式会去掉高位,但不会进行未定义行为。

我认为 gcc 警告可能试图表明所编写的代码并非完全 100% 可移植。有时确实应该编写代码以避免在某些实现上未定义的行为,但在许多其他情况下,人们应该简单地认为代码不太可能用于会做过于烦人的事情的实现。

请注意,使用和之类的类型int可能short会消除一些警告并修复一些问题,但可能会产生其他问题。uint16_t和 C 的整数提升规则这样的类型之间的交互很糟糕,但这样的类型仍然可能比任何其他类型都好。

1)如果你只是从无符号到相同长度的有符号整数来回转换,中间没有任何操作,你每次都会得到相同的结果,所以这里没有问题。但是各种逻辑和算术运算对有符号和无符号操作数的作用不同。
2) 使用类型的主要原因stdint.h是这种类型的位大小在所有平台上都是定义的并且相等,这对于等来说是不正确的int并且没有标准的符号,它可以被签名或无符号默认。在不使用额外检查和假设的情况下,可以更轻松地处理知道确切大小的数据。longchar

由于尤金的 #2 可能是最重要的一点,我只想补充一点,它是

MISRA (directive 4.6): "typedefs that indicate size and signedness should be used in place of the basic types".

Jack Ganssle 似乎也是该规则的支持者:http ://www.ganssle.com/tem/tem265.html

消除警告的一种简单方法是避免在 GCC 中使用 -Wconversion。我认为您必须手动启用此选项,但如果没有,您可以使用 -Wno-conversion 禁用它。如果您仍然需要,您可以通过其他选项启用符号和 FP 精度转换的警告。

-Wconversion 警告几乎总是误报,这可能就是为什么 -Wextra 默认情况下不启用它的原因。Stack Overflow 问题很多关于好的选项集的建议。根据我自己的经验,这是一个很好的起点:

-std=c99 -pedantic -Wall -Wextra -Wshadow

如果您需要它们,请添加更多,但很有可能您不会。

如果您必须保留 -Wconversion,您可以通过仅类型转换数字操作数来稍微缩短代码:

value16 <<= (uint16_t)8;
value8 += (uint8_t)2;

但是,如果没有语法突出显示,这并不容易阅读。