uint8_t,uint_fast8_t和uint_least8_t之间的区别

时间:2016-01-28 07:15:42

标签: c++ c integer avr c99

C99标准引入了以下数据类型。可以找到AVR stdint库的文档here

  • uint8_t表示它是8位无符号类型。
  • uint_fast8_t表示它是最快的unsigned int,至少为8 位。
  • uint_least8_t表示它是一个至少为8位的无符号整数。

我理解uint8_t以及什么是uint_fast8_t(我不知道它是如何在寄存器级别实现的。)

1.你能解释一下“它是unsigned int至少有8位”是什么意思吗?

2.与uint_fast8_t相比,uint_least8_tuint8_t如何帮助提高效率/代码空间?

7 个答案:

答案 0 :(得分:76)

uint_least8_t是至少有8位的最小类型。 uint_fast8_t是最快的类型,至少有8位。

您可以通过想象异国情调的建筑来看到差异。想象一下20位架构。其unsigned int有20位(一个寄存器),其unsigned char有10位。所以sizeof(int) == 2,但使用char类型需要额外的指令来将寄存器减半。然后:

  • uint8_t:未定义(无8位类型)。
  • uint_least8_t:是unsigned char,是至少8位的最小类型。
  • uint_fast8_t:是unsigned int,因为在我想象的架构中,半寄存器变量比完全寄存器变量慢。

答案 1 :(得分:23)

uint8_t表示:给我一个正好为8位的无符号整数。

uint_least8_t表示:给我最小类型的unsigned int,它至少有8位。优化内存消耗。

uint_fast8_t表示:给我一个至少为8位的无符号整数。选择更大的类型,如果它会使我的程序更快,因为对齐考虑因素。优化速度。

此外,与普通int类型不同,上述stdint.h类型的签名版本保证是2的补码格式。

答案 2 :(得分:23)

理论如下:

uint8_t必须正好是8位,但不需要存在。因此,您应该在依赖于8位整数的模数256分配行为*的地方使用它,并且您希望编译失败在不明显的体系结构上出现错误行为。

uint_least8_t必须是可存储至少8位的最小可用无符号整数类型。当你想最大限度地减少像大型数组这样的内存使用时,你会使用它。

uint_fast8_t应该是“最快”的无符号类型,可以存储至少8位;但是,对于任何给定处理器上的任何给定操作,它实际上并不保证是最快的。您可以在处理对该值执行大量操作的代码时使用它。

实践是“快速”和“最少”类型使用不多。

如果您关心使用CHAR_BIT!= 8模糊架构的可移植性,那么“最少”类型才真正有用。大多数人都不这样做。

“快速”类型的问题是“最快”很难确定。较小的类型可能意味着内存/缓存系统上的负载较小,但使用小于本机的类型可能需要额外的指令。此外,最好在架构版本之间进行更改,但实施者通常希望避免在这种情况下破坏ABI。

通过查看一些流行的实现,似乎 uint_fastn_t 的定义相当随意。 glibc似乎将它们定义为至少是所讨论系统的“本地字大小”,而不考虑许多现代处理器(尤其是64位处理器)对小于其本机字的项目的快速操作的特定支持。尺寸。 IOS显然将它们定义为与固定大小类型相同。其他平台可能会有所不同。

总而言之,如果您的目标是使用微小整数来实现紧密代码,那么您应该在您关注的平台上使用不同大小的类型标记您的代码以查看哪种方法效果最佳。

*请注意,遗憾的是模数256分配行为并不总是意味着模数为256的算术,这要归功于C的整数提升错误。

答案 3 :(得分:5)

某些处理器无法在较小的数据类型上高效运行。例如,给定:

uint32_t foo(uint32_t x, uint8_t y)
{
  x+=y;
  y+=2;
  x+=y;
  y+=4;
  x+=y;
  y+=6;
  x+=y;
  return x;
}

如果yuint32_t,则ARM Cortex-M3的编译器只能生成

add r0,r0,r1,asl #2   ; x+=(y<<2)
add r0,r0,#12         ; x+=12
bx  lr                ; return x

但由于yuint8_t,编译器必须生成:

add r0,r0,r1          ; x+=y
add r1,r1,#2          ; Compute y+2
and r1,r1,#255        ; y=(y+2) & 255
add r0,r0,r1          ; x+=y
add r1,r1,#4          ; Compute y+4
and r1,r1,#255        ; y=(y+4) & 255
add r0,r0,r1          ; x+=y
add r1,r1,#6          ; Compute y+6
and r1,r1,#255        ; y=(y+6) & 255
add r0,r0,r1          ; x+=y
bx  lr                ; return x

&#34; fast&#34;的预期目的类型是允许编译器替换不能用较快的类型有效处理的较小类型。不幸的是,&#34; fast&#34;的语义类型指定得很差,这反过来又留下了关于表达式是否会使用有符号或无符号数学进行评估的模糊问题。

答案 4 :(得分:4)

  

1.你能解释一下“它是一个至少有8位的无符号整数”是什么意思吗?

这应该是显而易见的。这意味着它是无符号整数类型,并且它的宽度至少为8位。实际上这意味着它至少可以保存数字0到255,并且它绝对不能保持负数,但它可以保存高于255的数字。

显然,如果您计划存储0到255范围之外的任何数字(并且希望它是可移植的),则不应使用任何这些类型。

  

2.与uint8_t相比,uint_fast8_t和uint_least8_t如何帮助提高效率/代码空间?

uint_fast8_t需要更快,所以如果你的要求是代码快,你应该使用它。另一方面,uint_least8_t要求没有较小尺寸的候选者 - 所以如果关注尺寸,你会使用它。

当你绝对要求它只是8位时,你当然只使用uint8_t。使用uint8_t可能会使代码不可移植,因为uint8_t不需要存在(因为在某些平台上不存在这种小整数类型)。

答案 5 :(得分:3)

&#34;快速&#34;整数类型被定义为可用的最快整数,至少需要所需的位数(在您的情况下为8)。

平台可以将uint_fast8_t定义为uint8_t,然后速度绝对没有差异。

原因是有些平台在不使用原生字长时速度较慢。

答案 6 :(得分:0)

我对局部变量和函数参数使用快速数据类型 (uint_fast8_t),并在经常使用的数组和结构中使用普通数据类型 (uint8_t),并且内存占用比可以节省的几个周期更重要通过不必清除或符号扩展高位。 效果很好,除了 MISRA 跳棋。他们从快速类型中发疯。诀窍在于,快速类型是通过派生类型使用的,这些派生类型可以为 MISRA 构建和普通构建定义不同。

我认为这些类型非常适合创建可移植代码,这在低端微控制器和大型应用处理器上都很有效。改进可能不是很大,或者对于好的编译器来说完全可以忽略不计,但总比没有好。