uint8_t,uint_fast8_t和uint_least8_t之间的区别

mic*_*mic 70 c c++ integer avr c99

C99标准引入了以下数据类型.可以在此处找到AVR stdint库的文档.

  • uint8_t 意味着它是一个8位无符号类型.
  • uint_fast8_t 意味着它是最快的无符号整数,至少有8位.
  • uint_least8_t 意味着它是一个至少8位的无符号整数.

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

你可以解释一下"它unsigned int至少有8位" 是什么意思吗?

2.How uint_fast8_tuint_least8_t相比有助于提高效率/代码空间uint8_t

rod*_*igo 87

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,因为在我想象的架构中,半寄存器变量比完全寄存器变量慢.

  • @Mehrdad例如,在ARM中,如果`int_fast8_t`是32位变量,则在算术运算之前不需要进行符号扩展. (14认同)
  • 我喜欢你必须想象异国情调的架构为此找到一个用例.他们在实践中发现了什么用处吗? (8认同)
  • @Mehrdad:我承认我从未见过真实世界应用程序中使用的`uint_leastX_t`或`uint_fastX_t`.`uintX_t`是的,它们被大量使用.看起来人们对异域架构的便携性并不十分有趣.这是预料之中的,即使你的未签名合适,你的程序也会在一千种不同的事情上失败. (7认同)
  • 例如@Mehrdad MIPS,让任何“uintX_fast_t”小于 32 位是非常错误的。你甚至不必想象架构让 `uint8_t` 变得未定义,以 UNIVAC 为例,它是 36 位,我假设 `char` 是 9 位。 (3认同)
  • @rodrigo 为什么人们不关心可移植性?我的经验是,主流架构的人们更倾向于假设每个行为略有不同的平台都是“损坏的”,并且不必关心“int”不适合保存指针。 (3认同)
  • @skyking:我不是说不应该使用它们,只是因为它们在实践中并没有被广泛使用.如果您能找到合理使用它们的真实应用程序或库,那么发布一个链接,因为我找不到任何链接. (3认同)
  • @Mehrdad:优势在于上下文。如果您编写“unsigned int”,则不清楚*为什么*您使用*该*类型(而不是其他类型)。是因为它“恰好”是 32 位,还是因为它“至少”是 32 位?还是因为那是“最快”的类型?(在*您的*平台上...)我——作为维护编码员——不知道您使用“unsigned int”的意图,并且我怀疑它可能是上下文中的错误类型(因此负责我正在寻找的错误)。如果您写了“uint_least32_t”,那么您的*意图*很明确,我可以仔细检查您的假设是否正确。 (2认同)

Lun*_*din 25

uint8_t 意思是:给我一个正好8位的无符号整数.

uint_least8_t意思是:给我最小类型的unsigned int,它至少有8位.优化内存消耗.

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

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

  • 请注意,使用2的补码格式只需要精确的宽度变量.另请注意,这些不是必需的.因此,不需要平台来支持2的补码格式. (6认同)

plu*_*ash 24

该理论类似于:

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的整数提升错误.

  • @zwol:我希望语言会添加根据布局和语义要求定义的类型类型,例如"我需要一些低位将别名为其他16位类型的东西,并且可以保存值0-65535,但我不喜欢不需要它将较大的值与该范围挂钩".别名,布局,范围和超出范围的行为应该是类型的四个独立方面,但C仅允许在不同平台之间不一致的某些组合. (4认同)
  • glibc的定义是在不存在这些优化的时候选择的,它们现在已经包含在ABI中,无法更改。这是_least和_fast类型实际上在实际中没有用的几个原因之一。 (2认同)

sup*_*cat 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;
}
Run Code Online (Sandbox Code Playgroud)

如果yuint32_tARM Cortex-M3的编译器可以简单地生成

add r0,r0,r1,asl #2   ; x+=(y<<2)
add r0,r0,#12         ; x+=12
bx  lr                ; return x
Run Code Online (Sandbox Code Playgroud)

但由于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
Run Code Online (Sandbox Code Playgroud)

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

  • 当处理较小的数据类型与较大的本机字大小的数据类型时,额外的潜在不必要的指令在很大程度上说明了为什么许多“快速”数据类型可能具有比预期更大的位宽度。谢谢你的例子。 (2认同)